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
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;
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>
)
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.).
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).
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