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.
Related
I am trying to write out a website with responsive-based app bar with a drawer tagged onto it. I am using this design found at https://medium.com/#tsubasakondo_36683/create-responsive-drawer-menu-with-react-material-ui-617a42764b69 and modifying it accordingly. But unfortunately, I am getting this issue, in which the drawer covers the portion of the content trying to be displayed. Please know that I have seen similar questions on this site, but I couldn't figure out how to solve them or the answers were kind of vague. Any help would be much appreciated, please let me know if I need to clarify anything further. All of this is using Material UI components.
Here is my code:
Material UI App-Bar
//Used code: https://medium.com/#tsubasakondo_36683/create-responsive-drawer-menu-with-react-material-ui-617a42764b69
//Picture/Icon Imports
import home from "../Icons/home.png";
import about from "../Icons/about.png";
import contact from "../Icons/contact.png";
import resume from "../Icons/resume.png";
import minecraft from "../Icons/minecraft.png";
import github from "../Icons/github.png"
import React from 'react';
import PropTypes from 'prop-types';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import MenuIcon from '#material-ui/icons/Menu';
import CloseIcon from '#material-ui/icons/Close';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import {Link} from "react-router-dom";
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth
},
content: {
flexGrow: 1,
padding: theme.spacing(0),
},
closeMenuButton: {
marginRight: 'auto',
marginLeft: 0,
},
}));
function NavBar() {
const DifferentPages = [
{Text: "Home", location: "/", Image: home},
{Text: "About", location: "/about", Image: about},
{Text: "Contact", location: "/contact", Image: contact},
{Text: "Resume", location: "/resume", Image: resume},
{Text: "Minecraft", location: "/minecraft", Image: minecraft},
]
const classes = useStyles();
const theme = useTheme();
const [mobileOpen, setMobileOpen] = React.useState(false);
function handleDrawerToggle() {
setMobileOpen(!mobileOpen)
}
const drawer = (
<div>
<List>
{DifferentPages.map((Text) => (
<ListItem button component={Link} to={Text.location}>
<img src={Text.Image} width={"25"} height={"25"}/>
<ListItemText primary={Text.Text} />
</ListItem>
))}
<ListItem button href={"example.com"}>
<img src={github} width={"25"} height={"25"}/>
<ListItemText>
Github
</ListItemText>
</ListItem>
</List>
</div>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="Open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Responsive drawer
</Typography>
</Toolbar>
</AppBar>
<nav className={classes.drawer}>
<Hidden smUp implementation="css">
<Drawer
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
<IconButton onClick={handleDrawerToggle} className={classes.closeMenuButton}>
<CloseIcon/>
</IconButton>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.toolbar} />
{drawer}
</Drawer>
</Hidden>
</nav>
<div className={classes.content}>
<div className={classes.toolbar} />
</div>
</div>
);
}
NavBar.propTypes = {
container: PropTypes.object,
};
export default NavBar;
App.js: NOTE** in App.js, I can add style={{paddingleft:240}} to the div before routes and it kind of remedy this issue, however, once the screen is small enough (i.e.like mobile) view and the drawer is hidden, 240px padding is still there.
(edited down for simplicity - content still the same)
import React from 'react';
import './App.css';
import { HashRouter as Router, Routes, Route} from 'react-router-dom';
//Page Routes
import NavigationAppBar from "./Navigation-Bar/NavigationAppBar";
import Home from './Page-Information/main';
function App() {
return (
<Router>
<NavigationAppBar/>
<div>
<Routes>
<Route exact path='/' exact element={<Home />} />
</Routes>
</div>
</Router>
);
}
export default App;
Home.js: (edited down for simplicity - content still the same)
import React from 'react';
const Home = () => {
return (
<div>
<h3>Example</h3>
</div>
);
};
export default Home;
I have forked the default persistent demo updated it into separate files.
You can add breakpoint for smaller device to remove margin or padding as appropriate.
const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })(
({ theme, open }) => ({
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginLeft: `-${drawerWidth}px`,
...(open && {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginLeft: 0
}),
[theme.breakpoints.down('sm')]: {
//add CSS here.
}
})
);
i have inertia progress bar initialized in my app.js
app.js
import React from "react";
import { render } from "react-dom";
import { createInertiaApp } from "#inertiajs/inertia-react";
import { InertiaProgress } from "#inertiajs/progress";
createInertiaApp({
resolve: (name) => require(`./Pages/${name}`),
setup({ el, App, props }) {
render(<App {...props} />, el);
},
});
InertiaProgress.init({
// The color of the progress bar.
color: "red",
// Whether the NProgress spinner will be shown.
showSpinner: true,
});
but the progressbar won't appear every time I render the appbar in the layout, its only the layout file, because if I render without it The progressbar will appear as usual
here is my Layout.tsx
import {
Button,
Container,
ListItem,
ListItemIcon,
ListItemText,
useTheme,
} from "#mui/material";
import React from "react";
import { styled, ThemeProvider } from "#mui/material/styles";
import CssBaseline from "#mui/material/CssBaseline";
import MuiDrawer from "#mui/material/Drawer";
import Box from "#mui/material/Box";
import MuiAppBar, { AppBarProps as MuiAppBarProps } from "#mui/material/AppBar";
import Toolbar from "#mui/material/Toolbar";
import List from "#mui/material/List";
import Typography from "#mui/material/Typography";
import Divider from "#mui/material/Divider";
import IconButton from "#mui/material/IconButton";
import MenuIcon from "#mui/icons-material/Menu";
import ChevronLeftIcon from "#mui/icons-material/ChevronLeft";
import { listItems } from "./ListItems";
import { usePage } from "#inertiajs/inertia-react";
import { Inertia } from "#inertiajs/inertia";
import route from "ziggy-js";
import FlashMessage from "./FlashMessage";
import { withStyles } from "#mui/styles";
const Layout = ({
children,
title,
}: {
children: React.ReactNode;
title: React.ReactNode;
}) => {
const theme = useTheme();
const authUser: any = usePage().props.user;
const drawerWidth: number = 240;
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const [open, setOpen] = React.useState(false);
const toggleDrawer = () => {
setOpen(!open);
};
const handleLogout = () => {
if (confirm("are you sure want to logout?")) {
Inertia.post(route("logout"));
}
};
return (
<ThemeProvider theme={theme}>
<Box sx={{ display: "flex" }}>
<CssBaseline />
<AppBar position="absolute" open={open}>
<Toolbar
sx={{
pr: "24px", // keep right padding when drawer closed
}}
>
<IconButton
edge="start"
color="inherit"
aria-label="open drawer"
onClick={toggleDrawer}
sx={{
marginRight: "36px",
...(open && { display: "none" }),
}}
>
<MenuIcon />
</IconButton>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
sx={{ flexGrow: 1, marginLeft: 2 }}
>
{title}
</Typography>
<Button
{...(authUser ? { display: "none" } : { sx: {} })}
variant="text"
color="inherit"
onClick={handleLogout}
>
Logout
</Button>
</Toolbar>
</AppBar>
<Drawer variant="permanent" open={open}>
<Toolbar
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
px: [1],
}}
>
<IconButton onClick={toggleDrawer}>
<ChevronLeftIcon />
</IconButton>
</Toolbar>
<Divider />
<List>{listItems}</List>
</Drawer>
<Box
component="main"
}}
>
<Toolbar />
<FlashMessage />
<Container sx={{ marginTop: "50px" }}>{children}</Container>
</Box>
</Box>
</ThemeProvider>
);
};
export default Layout;
I think my progressbar appears under the appbar, is there any solution to this problem?
Just find out what the zIndex for your AppBar is (1100 by default I think)
console.log(Theme.zIndex.appBar)
Then on your CSS file, add a line to make the zIndex of the progress bar higher:
#nprogress .bar {
z-index: 1101!important;
}
I am using material ui with react I want to know that
How can I change the background color of my card component when a user hovers?
Here is my live code link in codeasandbox.
Below I also put my code.
MyCard.js Here I want to change the background color on hover so the padding area changes the color
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Card from "#material-ui/core/Card";
import CardActionArea from "#material-ui/core/CardActionArea";
import CardMedia from "#material-ui/core/CardMedia";
const useStyles = makeStyles((theme) => ({
root: {
// maxWidth: 345,
margin: theme.spacing(0.5),
padding: theme.spacing(0.8),
borderRadius: theme.spacing(0),
"& :hover": {
backgroundColor: "green"
}
},
media: {
height: 140
}
}));
export default function MyCard() {
const classes = useStyles();
return (
<Card className={classes.root} elevation={3}>
<CardActionArea>
<CardMedia
className={classes.media}
image="https://images.pexels.com/photos/2787341/pexels-photo-2787341.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
title="Face"
/>
</CardActionArea>
</Card>
);
}
You should try omitting that space in on hover property, and write "&:hover" rather than "&(space):hover". It works fine in your sandbox code.
With the help of Y.S. I figured out it was just a typo having extra space near &
Here is the working code
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Card from "#material-ui/core/Card";
import CardActionArea from "#material-ui/core/CardActionArea";
import CardMedia from "#material-ui/core/CardMedia";
import { Paper } from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
root: {
// maxWidth: 345,
margin: theme.spacing(0.5),
padding: theme.spacing(0.8),
borderRadius: theme.spacing(0),
"&:hover": {
backgroundColor: "green"
}
},
media: {
height: 140
}
}));
export default function MyCard() {
const classes = useStyles();
return (
<Card className={classes.root} elevation={3}>
<CardActionArea>
<CardMedia
className={classes.media}
image="https://images.pexels.com/photos/2787341/pexels-photo-2787341.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
title="Face"
/>
</CardActionArea>
</Card>
);
}
I am new to Next js, I want to call the news api in useEffect and dispatch the news array to my store. then filter it based on the user's input in the search bar in the header. problem is once the use effect data fetching is done and user starts typing rather than filtering the news array the screen gets re-render and the data fetching starts again. how to prevent this re-rendering and save the news array?
I tried to use getStaticprops but useDispatch is not allowed in there.
index.js
import { fetchNews } from "../store/actions/newsActions";
import NewsInfo from "../components/NewsInfo";
import { useDispatch, useSelector } from "react-redux";
import CircularProgress from "#material-ui/core/CircularProgress";
export default function Home() {
const dispatch = useDispatch();
const { news } = useSelector((state) => state.news);
React.useEffect(() => {
dispatch(fetchNews());
}, []);
return (
<>
{/* this wrapper cmp will make each headline uniquely accessible */}
{news?.articles ? (
news.articles.map((article) => (
<React.Fragment key={article.publishedAt}>
<NewsInfo headlines={article} />
</React.Fragment>
))
) : (
<CircularProgress />
)}
</>
);
}
Header.js
import React from "react";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import Typography from "#material-ui/core/Typography";
import InputBase from "#material-ui/core/InputBase";
import { fade, makeStyles } from "#material-ui/core/styles";
import MenuIcon from "#material-ui/icons/Menu";
import SearchIcon from "#material-ui/icons/Search";
import { useDispatch } from "react-redux";
import { filterHeadlines } from "../store/actions/newsActions";
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
display: "none",
[theme.breakpoints.up("sm")]: {
display: "block",
},
},
search: {
position: "relative",
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
"&:hover": {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(1),
width: "auto",
},
},
searchIcon: {
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
inputRoot: {
color: "inherit",
},
inputInput: {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
"&:focus": {
width: "20ch",
},
},
},
}));
function SearchAppBar() {
const classes = useStyles();
const dispatch = useDispatch();
const [input, setInput] = React.useState("");
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="open drawer"
>
<MenuIcon />
</IconButton>
<Typography className={classes.title} variant="h6" noWrap>
Material-UI
</Typography>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
inputProps={{ "aria-label": "search" }}
value={input}
onChange={(e) => setInput(e.target.value)}
onBlur={() => dispatch(filterHeadlines(input))}
/>
</div>
</Toolbar>
</AppBar>
</div>
);
}
export default SearchAppBar;
you can use store dispatch to getServersideProps, getstaticprops
from your store file
export const rootDispatch = store.dispatch;
I am trying to make a react component to display a notification on use.
The thing is that it's not working correctly...
The notification is supposed to appear when a user logs-in with an email with wrong provider
what am I doing is that I check for user email after he logs in if the mail doesn't contain the right provider I log him out.
what is happening is that the notification is showing up and disappearing quickly after logging the user out though I made a long interval for it.
I tried experimenting with material UI the react UI framework.
Nothing is working so far
Navbar.js
import React, { Component } from "react";
import withFirebaseAuth from "react-with-firebase-auth";
import * as firebase from "firebase/app";
import firebaseConfig from "../../firebase.config";
import "firebase/auth";
// Material UI
import { makeStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import AccountCircle from "#material-ui/icons/AccountCircle";
import Tooltip from "#material-ui/core/Tooltip";
import Notify from "../notification/notification"
const firebaseApp = firebase.initializeApp(firebaseConfig);
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1
},
menuButton: {
marginRight: theme.spacing(2)
},
title: {
flexGrow: 1
}
}));
const filler = () => {console.log(' ')}
function HomeNav(props) {
const { user, signOut, signInWithGoogle } = props;
const classes = useStyles();
const userMail = (user)?user.email.includes("#hijrah.org"):'';
let notify = false;
if(user && !userMail){
signOut();
notify = true;
}
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
>
<MenuIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
News
</Typography>
{user? (
<Tooltip title={user.displayName}>
<IconButton
edge="end"
aria-label="account of current user"
aria-haspopup="true"
onClick={signOut}
color="inherit"
>
<AccountCircle />
</IconButton>
</Tooltip>
) : (
<Button color="inherit" onClick={signInWithGoogle}>
Login
</Button>
)}
</Toolbar>
</AppBar>
<React.Fragment>
{/*this is the part where i try to display notification on*/}
{(notify)? <Notify /> : <span></span>}
</React.Fragment>
</div>
);
}
class Nav extends Component {
render() {
return <HomeNav {...this.props} />;
}
}
const firebaseAppAuth = firebaseApp.auth();
const providers = {
googleProvider: new firebase.auth.GoogleAuthProvider()
};
providers.googleProvider.setCustomParameters({hd:"hijrah.org"});
export default withFirebaseAuth({
providers,
firebaseAppAuth
})(Nav);
my notification component notify.js
import React from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import CheckCircleIcon from "#material-ui/icons/CheckCircle";
import ErrorIcon from "#material-ui/icons/Error";
import InfoIcon from "#material-ui/icons/Info";
import CloseIcon from "#material-ui/icons/Close";
import { amber, green } from "#material-ui/core/colors";
import IconButton from "#material-ui/core/IconButton";
import Snackbar from "#material-ui/core/Snackbar";
import SnackbarContent from "#material-ui/core/SnackbarContent";
import WarningIcon from "#material-ui/icons/Warning";
import { makeStyles } from "#material-ui/core/styles";
const variantIcon = {
success: CheckCircleIcon,
warning: WarningIcon,
error: ErrorIcon,
info: InfoIcon
};
const useStyles1 = makeStyles(theme => ({
success: {
backgroundColor: green[600]
},
error: {
backgroundColor: theme.palette.error.dark
},
info: {
backgroundColor: theme.palette.primary.main
},
warning: {
backgroundColor: amber[700]
},
icon: {
fontSize: 20
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing(1)
},
message: {
display: "flex",
alignItems: "center"
}
}));
function MySnackbarContentWrapper(props) {
const classes = useStyles1();
const { className, message, onClose, variant, ...other } = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={clsx(classes[variant], className)}
aria-describedby="client-snackbar"
message={
<span id="client-snackbar" className={classes.message}>
<Icon className={clsx(classes.icon, classes.iconVariant)} />
{message}
</span>
}
action={[
<IconButton
key="close"
aria-label="close"
color="inherit"
onClick={onClose}
>
<CloseIcon className={classes.icon} />
</IconButton>
]}
{...other}
/>
);
}
MySnackbarContentWrapper.propTypes = {
className: PropTypes.string,
message: PropTypes.string,
onClose: PropTypes.func,
variant: PropTypes.oneOf(["error", "info", "success", "warning"]).isRequired
};
export default function CustomizedSnackbars() {
const [open, setOpen] = React.useState(true);
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
return (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
autoHideDuration={60000}
onClose={handleClose}
>
<MySnackbarContentWrapper
onClose={handleClose}
variant="warning"
message="Please login with a #hijrah.org mail"
/>
</Snackbar>
</div>
);
}
I expect to display the error message after I log out the user and for it to stay for like 5 seconds.
I suspect what is happening in that when you log the user out, the Nav component is no longer rendered and therefore neither is the Notify Component as it is a child of Nav (Nav >> HomeNav >> Notify). Because it is no longer rendered the message disappears.
If that is the case, you should move the Notify to a higher level in your app so that it is displayed regardless of whether the user is logged in or not.