Related
I'm adding a MaterialUI Tooltip to my React app and I need to have two text lines.
One will be treated as the title main text the other one as a sublabel.
Here in the screenshot, you can see where I stuck
The goal is to have it all in one card as
The label should read: Self-assessment opened: [n]
Sub-label: Click to drill down
An example from the Figma I have which shows how the card should look
I'm new to MUI and not sure how I should do it and this is the code I have so far
import { Box, Popper, Tooltip } from '#mui/material';
import { styled } from '#mui/material/styles';
import { useTheme } from '#mui/styles';
import { useIntl } from 'react-intl';
// Use styles from src/analytics/components/TooltipCard/TooltipCard.js to make it look the same
const TooltipCardPopper = styled(Popper)(({ theme }) => ({
'& > div': {
...theme.typography.caption,
backgroundColor: 'white',
boxShadow: theme.shadows[7],
borderRadius: '5px',
paddingLeft: theme.spacing(1.2),
paddingRight: theme.spacing(1.2),
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
borderWidth: 1,
borderStyle: 'solid',
borderColor: theme.palette.grey[300],
},
}));
const calculateTotals = ({ data }) =>
data?.reduce(function (accumulator, item) {
return accumulator + item.total;
}, 0);
const CenteredMetricToolTip = ({ position, data }) => {
const theme = useTheme();
const intl = useIntl();
const show = !!position;
const total = calculateTotals({ data });
return (
<Tooltip
open={show}
disableFocusListener
disableHoverListener
disableTouchListener
disableInteractive
title={intl.formatMessage({ defaultMessage: 'Click to drill down' })}
PopperComponent={TooltipCardPopper}
TransitionProps={{ timeout: 0 }} // timeout more than 0 => transition => causes re-positioning and blinking
>
<Box
sx={{
position: 'absolute',
display: show ? 'block' : 'hide',
left: `${position?.x ?? 0}px`,
top: `${position?.y ?? 0}px`,
}}
>
{intl.formatMessage(
{ defaultMessage: ' Self-assessment opened: {total}' },
{ total },
)}
</Box>
</Tooltip>
);
};
export default CenteredMetricToolTip;
try to change title property value from title={intl.formatMessage({ defaultMessage: 'Click to drill down' })} to
title={
<div>
<div>{intl.formatMessage({ defaultMessage:"Self-assessment opened: [n]" })}<div>
<div>{intl.formatMessage({ defaultMessage:"Click to drill down"})}</div>
<div>
}
on Tooltip Component
I have created a Material-UI persistent drawer in which there is a list item component that aims to change the icon color whenever a user clicks on the list item. But my styling is only working with Material-UI icon, not with external SVG.
Here is codesandbox link for the same project to understand it better.
Here is my AppBarDrawer.js parent component that renders my listItem component. Working fine and can be ignored
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";
import DrawerList from "./components/DrawerList";
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>
<DrawerList />
</List>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
<Typography paragraph>
Lorem Nulla posuere sollicitudin aliquam ultrices sagittis orci a
</Typography>
</main>
</div>
);
}
The Main file DrawerList.js which is not giving desired out
Here the real issue is my external icons color is not changing to white whenever I click on it however the last icon named ExitToAppOutlined is a Material-UI icon and is working fine on click.
import React, { useState } from "react";
import ListItem from "#material-ui/core/ListItem";
import Link from "#material-ui/core/Link";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import { ExitToAppOutlined } from "#material-ui/icons";
import ListItemText from "#material-ui/core/ListItemText";
import { useStyles } from "./DrawerListStyle";
import Typography from "#material-ui/core/Typography";
import Box from "#material-ui/core/Box";
import { SvgIcon } from "#material-ui/core";
import { ReactComponent as Appointment } from "../../assets/Appointment.svg";
import { ReactComponent as Customers } from "../../assets/manage customers 2.svg";
const itemList = [
{
text: "Book Appointment",
icon: (
<SvgIcon>
{/* external icons as svg */}
<Appointment />
</SvgIcon>
)
},
{
text: "Manage",
icon: (
<SvgIcon>
{/* external icons as svg */}
<Customers />
</SvgIcon>
)
},
{
text: "Logout",
// Material Icons
icon: <ExitToAppOutlined />
}
];
const DrawerList = () => {
const [selectedIndex, setSelectedIndex] = useState(0);
const classes = useStyles();
const ListData = () =>
itemList.map((item, index) => {
const { text, icon } = item;
return (
<ListItem
button
key={text}
component={Link}
selected={index === selectedIndex}
onClick={(e) => handleListItemClick(e, index)}
style={selectedIndex === index ? { backgroundColor: "#6A2CD8" } : {}}
>
<ListItemIcon
className={classes.iconStyle}
style={selectedIndex === index ? { color: "#fff" } : {}}
>
{icon}
<ListItemText>
<Typography
component="div"
className={classes.iconTitle}
style={selectedIndex === index ? { color: "#fff" } : {}}
>
<Box fontWeight={500} fontSize={13.5}>
{text}
</Box>
</Typography>
</ListItemText>
</ListItemIcon>
</ListItem>
);
});
const handleListItemClick = (e, index) => {
setSelectedIndex(index);
};
return (
<div className={classes.root}>
<ListData />
</div>
);
};
export default DrawerList;
DrawerListStyle.js just an stylejs file and can be ignored
import { makeStyles } from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
root: {
marginTop: theme.spacing(2)
},
iconStyle: {
margin: theme.spacing(0, 0, 1, 0),
color: "#6A2CD8"
},
iconTitle: {
margin: theme.spacing(0, 0, 0, 1),
color: "#555458"
}
}));
export { useStyles };
Material-UI sets the color of your ListItemIcon when the ListItem is selected, but because your custom svg icons already have the fill attribute set to another color, it overrides the color from MUI. The fix is simple, override the fill attribute again in your custom svg using makeStyles:
const useStyles = makeStyles((theme) => ({
{...}
listItem: {
"&.Mui-selected": {
"& path": {
fill: "white"
}
}
}
}));
<ListItem className={classes.listItem}
Live Demo
you can also use SVGR to build your own icons.
you can follow these steps:
1- install svgr using
npm install --save-dev #svgr/cli
# or use yarn
yarn add --dev #svgr/cli
2- add your icons in public/icons
3- create a file named svgr.config.js and place it in the root of project
module.exports = {
icon: true,
dimensions: false,
expandProps: false,
replaceAttrValues: {
'#000': 'currentColor',
'#292D32': 'currentColor',
'#292d32': 'currentColor',
'#55BB9D': 'currentColor',
'#FFBC50': 'currentColor',
'#A7A7A7': 'currentColor'
}
};
4- add script in package.json
"svg": "svgr --ext=jsx -d src/#share/icons public/icons"
5 - run yarn svg.
once you run above command svgr grab all icons in the public folder and gerenate React component inside src/#share/icons folder based on the config you provided.
and replace every color text of svg into current color.
in this case replace every key in replaceAttrValues to the currentColor.
then in your react component you can simply do this:
import { MyIcon } from '#share/icons';
import { SvgIcon } from '#mui/material';
function MyComponent() {
return <p><SvgIcon component={MyIcon} /> check this icon </p>
};
export default MyComponent;
in this way you can change the color the way you change the text color of p element.
I'm still new to React so I have this project which has a drawer component with several routes (Dashboard, Chart, and User Settings). Under the Dashboard component (which also consists of the Navbar and Drawer component), I have a TablePage, ChartPage and SettingsPage components which will be rendered based from the route.
When I log in, it will render the table component in the /dashboard which gets the data from an API. If I click Chart from the drawer, it will reroute to /chart.
Currently it will call an API which will get the information when the dashboard component is loaded (/dashboard) and it will pass all necessary data (props) to the child component in ChartPage and SettingsPage. The problem is, if the database is updated, I will need to refresh the page in order to get the updated data. What I want to do, is whenever the user clicks on the button from the drawer, it will call the API to get the updated value.
In order to that, I'm using the useContext hooks which will pass the readData function to the TablePage component. How do I render it once if there is no information is updated and will render the component if there is an updated information from the database? Apparently it says that the readData is not a function even though I retrieve it from the useContext method in TablePage.js.
Dashboard.js
import clsx from "clsx";
import Drawer from "#material-ui/core/Drawer";
import MenuIcon from "#material-ui/icons/Menu";
import ChevronLeftIcon from "#material-ui/icons/ChevronLeft";
import DrawerHeader from "./DrawerHeader";
import DrawerContent from "./DrawerContent";
import React, { useState, useContext, useEffect, Fragment } from "react";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
// import RefreshIcon from "#material-ui/icons/Refresh";
// import Badge from "#material-ui/core/Badge";
// import NotificationsIcon from "#material-ui/icons/Notifications";
import ToggleMenu from "./ToggleMenu";
import Loader from "./Loader";
import axios from "axios";
import AuthApi from "../utils/createContext";
import moment from "moment";
import PageRoute from "./PageRoute";
import DataApi from "../utils/createContext";
const m = moment();
const today = m.format("LL");
// function refresh() {
// setTimeout(function () {
// window.location.reload();
// }, 100);
// }
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
},
listContainer: {
display: "flex",
},
toolbar: {
paddingRight: 24, // keep right padding when drawer closed
},
toolbarIcon: {
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
padding: "0 8px",
...theme.mixins.toolbar,
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginRight: 36,
},
menuButtonHidden: {
display: "none",
},
title: {
flexGrow: 1,
},
drawerPaper: {
position: "relative",
whiteSpace: "nowrap",
width: drawerWidth,
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerPaperClose: {
overflowX: "hidden",
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing(7),
[theme.breakpoints.up("sm")]: {
width: theme.spacing(9),
},
},
appBarSpacer: theme.mixins.toolbar,
// content: {
// flexGrow: 1,
// height: "100vh",
// overflow: "auto",
// },
paper: {
padding: theme.spacing(2),
display: "flex",
overflow: "auto",
flexDirection: "column",
},
fixedHeight: {
height: 240,
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
// marginLeft: "-70px",
},
contentShift: {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
},
}));
export default function Dashboard2() {
const classes = useStyles();
const [open, setOpen] = useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const { value } = useContext(AuthApi);
//get user and customer api
const [userData, setUserData] = useState({});
const [customerData, setCustomerData] = useState([]);
const [customerCount, setCustomerCount] = useState();
const [isFetching, setIsFetching] = useState(false);
const readData = async () => {
const fetchUserInfo = await axios.post(`/auth/getemail/${value}`);
console.log(fetchUserInfo);
// console.log(result.data.user[0]._id);
const { data } = fetchUserInfo;
const { user } = data;
let newUserData = {};
user.forEach((cData) => {
newUserData = {
joindate: cData.joindate,
email: cData.email,
adminId: cData.adminId,
_id: cData._id,
};
});
// console.log(newUserData);
setUserData(newUserData);
const fetchCustomerInfo = await axios.post(
`/customerinfo/getcustomer/${fetchUserInfo.data.user[0].adminId}`
);
// console.log(fetchCustomerInfo);
const cust = fetchCustomerInfo.data;
// console.log(cust.customer_info);
// console.log(cust.count);
setCustomerData(cust.customer_info);
setCustomerCount(cust.count);
setIsFetching(true);
};
useEffect(() => {
readData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// const username = value.slice(0, value.search("#"));
return (
<Fragment>
{isFetching ? (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="absolute"
className={clsx(classes.appBar, open && classes.appBarShift)}
>
<Toolbar className={classes.toolbar}>
<IconButton
edge="start"
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
className={clsx(
classes.menuButton,
open && classes.menuButtonHidden
)}
>
<MenuIcon />
</IconButton>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
className={classes.title}
>
Dashboard
</Typography>
{/* <IconButton color="inherit" onClick={refresh}>
<RefreshIcon />
</IconButton> */}
{/* <IconButton color="inherit">
<Badge badgeContent={4} color="secondary">
<NotificationsIcon />
</Badge>
</IconButton> */}
<ToggleMenu />
</Toolbar>
</AppBar>
<Drawer
variant="persistent" //persistent,permanent,temporary
anchor="left"
classes={{
paper: clsx(
classes.drawerPaper,
!open && classes.drawerPaperClose
),
}}
open={open}
>
<div className={classes.toolbarIcon}>
<IconButton onClick={handleDrawerClose}>
<ChevronLeftIcon />
</IconButton>
</div>
<DrawerHeader
email={userData.email}
joindate={moment(userData.joindate).format("LL")}
id={userData._id}
/>
<DrawerContent />
</Drawer>
<DataApi.Provider
value={{
customerData,
customerCount,
userData,
isFetching,
readData,
}}
>
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
<div className={classes.appBarSpacer} />
<PageRoute
customerdata={customerData}
userdata={userData}
customercount={customerCount}
momentdate={today}
/>
</main>
</DataApi.Provider>
</div>
) : (
<Loader type={"Bars"} color={"#9370DB"} />
)}
</Fragment>
);
}
TablePage.js
import React, { useContext, useEffect, Fragment } from "react";
import MaterialTable from "material-table";
import { Container, Grid } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import DataApi from "../utils/createContext";
import Loader from "./Loader";
const useStyles = makeStyles((theme) => ({
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4),
},
}));
export default function TablePage({ customerdata }) {
const classes = useStyles();
const { readData, isFetching } = useContext(DataApi);
useEffect(() => {
readData();
}, []);
return (
<Fragment>
{isFetching ? (
<div>
<Container maxWidth="lg" className={classes.container}>
<Grid>
<MaterialTable
columns={[
{ title: "Customer Name", field: "fullname" },
{ title: "Phone No.", field: "phone", type: "string" },
{ title: "Address", field: "address" },
{ title: "Date", field: "updated", type: "date" }, //Data type: 'boolean', 'numeric', 'date', 'datetime', 'time', 'currency'
{ title: "Time", field: "updated", type: "time" },
]}
data={customerdata}
title="Check-In Information"
options={{
exportButton: true,
filtering: true,
}}
/>
</Grid>
</Container>
</div>
) : (
<Loader type={"Bars"} color={"#9370DB"} />
)}
</Fragment>
);
}
UPDATED: DataApi.Provider location is changed to wrap only the Routes component and the readData dependencies on the useEffect for Table.js has been removed
I am a beginner with react. I get an error like this:
TypeError: Cannot set property 'props' of undefined
if I delete this code the props will work :
export default compose(
withStyles(styles,{name:'MainLayout'}),
connect(mapStateToProps,{logoutAdmin})
(MainLayout));
replace this code with this:
export default withStyles(styles)(MainLayout);
is it because the props are not sent? or an error occurred in the code?
sorry I don't understand what I have to do, I have already followed the tutorial and it has been checking for hours and the results are still not working
this is my code :
import React from 'react';
import compose from 'recompose/compose';
import { withStyles } from '#material-ui/core/styles';
import { AppBar, IconButton,Button, Toolbar, Typography, Drawer, List,Paper } from '#material-ui/core';
import InboxIcon from '#material-ui/icons/Inbox';
import classNames from 'classnames';
import MenuIcon from '#material-ui/icons/Menu';
import FaceIcon from '#material-ui/icons/Face';
import PowerSettingsNewIcon from '#material-ui/icons/PowerSettingsNew';
import { Link } from 'react-router-dom';
import { mainListItems } from './ListItem.js';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { logoutAdmin } from '../../actions/authActions';
const drawerWidth = 260;
const styles = theme => ({
root: {
display: 'flex',
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
whiteSpace: 'nowrap',
width: drawerWidth,
flexShrink: 0,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
appBar: {
zIndex: theme.zIndex.drawer + 1, //agar z index MainLayout lebih tinggi dari navbar
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
toolbar:theme.mixins.toolbar,
drawerPaperClose: {
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing.unit * 7,
[theme.breakpoints.up('sm')]: {
width: theme.spacing.unit * 9,
},
},
content: {
flexGrow: 1,
padding: theme.spacing.unit * 5,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginLeft: -drawerWidth+40,
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
},
grow: {
flexGrow: 1,
},
});
class MainLayout extends React.Component {
constructor() {
super();
this.state = {
open: true,
Menu: [
{ title: "Dashboard", icon: "DashboardIcon", link: "/" },
{ title: "Inbox", icon: "MenuIcon", link: "/inbox" },
],
};
}
handleDrawerOpen = () => {
this.setState({ open: !this.state.open });
};
onLogoutClick = () => {
this.props.logoutAdmin();
}
render() {
const { classes } = this.props;
const { open } = this.state;
return (
<div className={classes.root}>
<AppBar position="fixed" className={classes.appBar} >
<Toolbar disableGutters={!this.state.open} className={classes.toolbar} >
<IconButton className={classes.menuButton} onClick={this.handleDrawerOpen} className={classNames(
classes.menuButton,
this.state.open && classes.menuButtonHidden,
)} color="inherit" aria-label="Open drawer">
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" noWrap>
Bimbingan Konseling
</Typography>
<div className={classes.grow} />
<Button
color="inherit"
component={Link} to='/login'
style={{textTransform:"none"}}
>
<FaceIcon />
<Typography variant="subtitle1" color="inherit" style={{marginLeft:10}}>
Username
</Typography>
</Button>
<Button
color="inherit"
component={Link} to='/login'
style={{ textTransform: "none" }}
>
<PowerSettingsNewIcon />
<Typography variant="subtitle1" color="inherit" onClick={this.onLogoutClick} style={{marginLeft:10}}>
Logout
</Typography>
</Button>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawerPaper}
variant="permanent"
classes={{
paper: classNames(classes.drawerPaper, !this.state.open && classes.drawerPaperClose)
}}
>
<div className={classes.toolbar} />
<List >
{mainListItems}
</List>
<div />
</Drawer>
<main className={classNames(classes.content, {
[classes.contentShift]: open,
})}>
<div style={{marginTop:40}}>
{this.props.children}
</div>
</main>
</div>
);
}
}
MainLayout.propTypes = {
classes: PropTypes.object.isRequired,
logoutAdmin:PropTypes.func.isRequired,
auth:PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
auth: state.auth
});
// Menggunakan library recompose
export default compose(
withStyles(styles,{name:'MainLayout'}),
connect(mapStateToProps,{logoutAdmin})
(MainLayout));
You haven't used compose syntax correctly. You should pass the component as the argument to the returned function by compose
export default compose(
withStyles(styles,{name:'MainLayout'}),
connect(mapStateToProps,{logoutAdmin})
)(MainLayout);
U can try use compose of redux and use like this.
import { connect } from "react-redux";
import { compose } from "redux";
import { withStyles } from "#material-ui/core/styles";
// Your code
export default compose(
connect(mapStateToProps),
)(withStyles(styles)(MainLayout));
I have one, larger component that has several state and props. This component is responsible for the header and sidebar. (moves the menu to the left). so I want to split it into two files, but I can not do it well.
This component has const Drawer and that's exactly what I would like to move to another file (along with state and all styles)
This is my one, larger component:
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import classNames from 'classnames';
import Drawer from '#material-ui/core/Drawer';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
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 ListItem from '#material-ui/core/ListItem';
import List from '#material-ui/core/List';
// import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
const drawerWidth = 240;
const styles = theme => ({
root: {
flexGrow: 1
},
appFrame: {
height: 430,
zIndex: 1,
overflow: 'hidden',
position: 'relative',
display: 'flex',
width: '100%'
},
appBar: {
position: 'absolute',
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
'appBarShift-left': {
marginLeft: drawerWidth
},
'appBarShift-right': {
marginRight: drawerWidth
},
menuButton: {
marginLeft: 12,
marginRight: 20
},
hide: {
display: 'none'
},
drawerPaper: {
position: 'relative',
width: drawerWidth
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
'content-left': {
marginLeft: -drawerWidth
},
'content-right': {
marginRight: -drawerWidth
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
'contentShift-left': {
marginLeft: 0
},
'contentShift-right': {
marginRight: 0
}
});
export default withStyles(styles, { withTheme: true })(
class Header extends Component {
state = {
open: true,
anchor: 'left'
};
handleDrawerToggle = () => {
const open = this.state.open;
this.setState({
open: !open
});
};
render() {
const { classes, theme } = this.props;
const { anchor, open } = this.state;
const drawer = (
<Drawer
variant="persistent"
anchor={anchor}
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader} />
<Divider />
<List component="nav">
<ListItem button>
<ListItemText primary="One ListItem" />
</ListItem>
<ListItem button>
<ListItemText primary="Two ListItem" />
</ListItem>
</List>
</Drawer>
);
return (
<div className={classes.appFrame}>
<AppBar
className={classNames(classes.appBar, {
[classes.appBarShift]: open,
[classes[`appBarShift-${anchor}`]]: open
})}
>
<Toolbar disableGutters={!open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerToggle}
className={classNames(classes.menuButton)}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
Example Text
</Typography>
</Toolbar>
</AppBar>
{drawer}
<main
className={classNames(classes.content, classes[`content-${anchor}`], {
[classes.contentShift]: open,
[classes[`contentShift-${anchor}`]]: open
})}
>
<div className={classes.drawerHeader} />
<Typography>You think water moves fast? You should see ice.</Typography>
</main>
</div>
);
}
}
);
And this is my attempt to split this into two files, but it does not work.
It compiles success, but it does not display correctly.
Header.js:
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import classNames from 'classnames';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import Sidebar from './Sidebar';
const drawerWidth = 240;
const styles = theme => ({
root: {
flexGrow: 1
},
appFrame: {
height: 430,
zIndex: 1,
overflow: 'hidden',
position: 'relative',
display: 'flex',
width: '100%'
},
appBar: {
position: 'absolute',
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
'appBarShift-left': {
marginLeft: drawerWidth
},
'appBarShift-right': {
marginRight: drawerWidth
},
menuButton: {
marginLeft: 12,
marginRight: 20
},
hide: {
display: 'none'
},
drawerPaper: {
position: 'relative',
width: drawerWidth
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
'content-left': {
marginLeft: -drawerWidth
},
'content-right': {
marginRight: -drawerWidth
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
'contentShift-left': {
marginLeft: 0
},
'contentShift-right': {
marginRight: 0
}
});
export default withStyles(styles, { withTheme: true })(
class Header extends Component {
state = {};
handleDrawerToggle = () => {
const open = this.props.open;
this.setState({
open: !open
});
};
render() {
const { classes, theme } = this.props;
const { anchor, open } = this.props;
return (
<div className={classes.appFrame}>
<AppBar
className={classNames(classes.appBar, {
[classes.appBarShift]: open,
[classes[`appBarShift-${anchor}`]]: open
})}
>
<Toolbar disableGutters={!open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerToggle}
className={classNames(classes.menuButton)}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
Example Text
</Typography>
</Toolbar>
</AppBar>
<Sidebar />
<main
className={classNames(classes.content, classes[`content-${anchor}`], {
[classes.contentShift]: open,
[classes[`contentShift-${anchor}`]]: open
})}
>
<div className={classes.drawerHeader} />
<Typography>You think water moves fast? You should see ice.</Typography>
</main>
</div>
);
}
}
);
Sidebar.js:
import React, { Component } from 'react';
import ListItem from '#material-ui/core/ListItem';
import List from '#material-ui/core/List';
import { withStyles } from '#material-ui/core/styles';
// import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import Drawer from '#material-ui/core/Drawer';
import Divider from '#material-ui/core/Divider';
const drawerWidth = 240;
const styles = theme => ({
drawerPaper: {
position: 'relative',
width: drawerWidth
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar
}
});
export default withStyles(styles, { withTheme: true })(
class Sidebar extends Component {
state = {
open: true,
anchor: 'left'
};
render() {
const { classes, theme } = this.props;
const { anchor, open } = this.state;
return (
<Drawer
variant="persistent"
anchor={anchor}
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader} />
<Divider />
<List component="nav">
<ListItem button>
<ListItemText primary="One ListItem" />
</ListItem>
<ListItem button>
<ListItemText primary="Two ListItem" />
</ListItem>
</List>
</Drawer>
);
}
}
);
You are setting the state open in your header component but you are not passing it down to Sidebar component as a prop to control it.
I'm suposing that you choose the Header Component to manage the open state property soo:
your header component should be with open and it's function to toggle.
class Header extends Component {
state = {
open: true,
};
handleDrawerToggle = () => {
const open = this.state.open;
this.setState({
open: !open
});
};
render() { ...
<Sidebar open={this.state.open} anchor="left" />
}
}
and sidebar component should receive the other stuffs from props
const Sidebar = () => {
const { classes, theme, anchor, open } = this.props;
return (
...
)
};
You are not passing down the props, that could be the issue. But something like this should work.
The best way to accomplish this is by using stateless components and pass down the data you need it to render.
You can write the main component like this:
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import classNames from 'classnames';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
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 Sidebar from './Sidebar';
const drawerWidth = 240;
const styles = theme => ({
root: {
flexGrow: 1
},
appFrame: {
height: 430,
zIndex: 1,
overflow: 'hidden',
position: 'relative',
display: 'flex',
width: '100%'
},
appBar: {
position: 'absolute',
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
'appBarShift-left': {
marginLeft: drawerWidth
},
'appBarShift-right': {
marginRight: drawerWidth
},
menuButton: {
marginLeft: 12,
marginRight: 20
},
hide: {
display: 'none'
},
drawerPaper: {
position: 'relative',
width: drawerWidth
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
'content-left': {
marginLeft: -drawerWidth
},
'content-right': {
marginRight: -drawerWidth
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
'contentShift-left': {
marginLeft: 0
},
'contentShift-right': {
marginRight: 0
}
});
export default withStyles(styles, { withTheme: true })(
class Header extends Component {
state = {
open: true,
anchor: 'left'
};
handleDrawerToggle = () => {
const open = this.state.open;
this.setState({
open: !open
});
};
render() {
const { classes, theme } = this.props;
const { anchor, open } = this.state;
return (
<div className={classes.appFrame}>
<AppBar
className={classNames(classes.appBar, {
[classes.appBarShift]: open,
[classes[`appBarShift-${anchor}`]]: open
})}
>
<Toolbar disableGutters={!open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerToggle}
className={classNames(classes.menuButton)}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" noWrap>
Example Text
</Typography>
</Toolbar>
</AppBar>
<Sidebar anchor={anchor} open={open} classes={classes} />
<main
className={classNames(classes.content, classes[`content-${anchor}`], {
[classes.contentShift]: open,
[classes[`contentShift-${anchor}`]]: open
})}
>
<div className={classes.drawerHeader} />
<Typography>You think water moves fast? You should see ice.</Typography>
</main>
</div>
);
}
}
);
And the Sidebar component like:
import Drawer from '#material-ui/core/Drawer';
import ListItem from '#material-ui/core/ListItem';
import List from '#material-ui/core/List';
import ListItemText from '#material-ui/core/ListItemText';
const Sidebar = ({ anchor, open, classes }) => (
<Drawer
variant="persistent"
anchor={anchor}
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader} />
<Divider />
<List component="nav">
<ListItem button>
<ListItemText primary="One ListItem" />
</ListItem>
<ListItem button>
<ListItemText primary="Two ListItem" />
</ListItem>
</List>
</Drawer>
);