Logo not showing in AppBar (Material UI / React) - reactjs

The logo is not showing on my AppBar for some reason. Here's what I am getting:
This is my code:
import {
AppBar,
Toolbar,
Typography,
makeStyles,
Button,
IconButton,
Drawer,
Link,
MenuItem,
} from "#material-ui/core";
import MenuIcon from "#material-ui/icons/Menu";
import React, { useState, useEffect } from "react";
import { Link as RouterLink } from "react-router-dom";
import logo from '../overland-ninja-logo.png';
const headersData = [
{
label: "Listings",
href: "/listings",
},
{
label: "Mentors",
href: "/mentors",
},
{
label: "My Account",
href: "/account",
},
{
label: "Log Out",
href: "/logout",
},
];
const useStyles = makeStyles(() => ({
header: {
backgroundColor: "#400CCC",
paddingRight: "79px",
paddingLeft: "118px",
"#media (max-width: 900px)": {
paddingLeft: 0,
},
},
logo: {
fontFamily: "Work Sans, sans-serif",
fontWeight: 600,
color: "#FFFEFE",
textAlign: "left",
},
menuButton: {
fontFamily: "Open Sans, sans-serif",
fontWeight: 700,
size: "18px",
marginLeft: "38px",
},
toolbar: {
display: "flex",
justifyContent: "space-between",
},
drawerContainer: {
padding: "20px 30px",
},
}));
export default function Header() {
const { header, logo, menuButton, toolbar, drawerContainer } = useStyles();
const [state, setState] = useState({
mobileView: false,
drawerOpen: false,
});
const { mobileView, drawerOpen } = state;
useEffect(() => {
const setResponsiveness = () => {
return window.innerWidth < 900
? setState((prevState) => ({ ...prevState, mobileView: true }))
: setState((prevState) => ({ ...prevState, mobileView: false }));
};
setResponsiveness();
window.addEventListener("resize", () => setResponsiveness());
}, []);
const displayDesktop = () => {
return (
<Toolbar className={toolbar}>
{femmecubatorLogo}
<div>{getMenuButtons()}</div>
</Toolbar>
);
};
const displayMobile = () => {
const handleDrawerOpen = () =>
setState((prevState) => ({ ...prevState, drawerOpen: true }));
const handleDrawerClose = () =>
setState((prevState) => ({ ...prevState, drawerOpen: false }));
return (
<Toolbar>
<IconButton
{...{
edge: "start",
color: "inherit",
"aria-label": "menu",
"aria-haspopup": "true",
onClick: handleDrawerOpen,
}}
>
<MenuIcon />
</IconButton>
<Drawer
{...{
anchor: "left",
open: drawerOpen,
onClose: handleDrawerClose,
}}
>
<div className={drawerContainer}>{getDrawerChoices()}</div>
</Drawer>
<div>{femmecubatorLogo}</div>
</Toolbar>
);
};
const getDrawerChoices = () => {
return headersData.map(({ label, href }) => {
return (
<Link
{...{
component: RouterLink,
to: href,
color: "inherit",
style: { textDecoration: "none" },
key: label,
}}
>
<MenuItem>{label}</MenuItem>
</Link>
);
});
};
const femmecubatorLogo = (
<img src={logo} />
);
const getMenuButtons = () => {
return headersData.map(({ label, href }) => {
return (
<Button
{...{
key: label,
color: "inherit",
to: href,
component: RouterLink,
className: menuButton,
}}
>
{label}
</Button>
);
});
};
return (
<header>
<AppBar className={header}>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</header>
);
}
I am quite new to react and I am sort of confused about how to go about this. I have tried all the tutorials but can't find the right solution. Any help would be much appreciated.

You are reassigning the value of logo after importing the image.
import logo from "../overland-ninja-logo.png"; // value of logo is correctly assigned to a path to the image
...
const useStyles = makeStyles(() => ({
logo: {
fontFamily: "Work Sans, sans-serif",
fontWeight: 600,
color: "#FFFEFE",
textAlign: "left"
},
}));
...
const { header, logo, menuButton, toolbar, drawerContainer } = useStyles(); // value of logo is incorrectly assigned to a an object
If you are using vscode, you should get a typescript warning at import logo:
'logo' is declared but its value is never read.ts(6133)

Related

Dark Mode in react using MUI v5

Trying to create a Toggle to switch from dark mode to light mode has been quite difficult for me in v5.
Using the code directly from MUI Sandbox MUI darkmode, I attempted to separate the code to work in the app.js and my Navbarpractice.js.
App.js
import React from "react";
import useMediaQuery from '#mui/material/useMediaQuery';
import { createTheme, ThemeProvider } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import { Paper } from "#mui/material";
import BasicCard from "./components /Card.js";
import Navbarpractice from "./components /Navbar/Navbarpractice"
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
function App() {
const [mode, setMode] = React.useState('light');
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
},
}),
[],
);
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode],
);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<div className="App">
<Navbarpractice/>
<BasicCard/>
</div>
</ThemeProvider>
</ColorModeContext.Provider>
);
}
export default App;
Navbarpractice.js
import React from 'react';
import AppBar from '#mui/material/AppBar';
import Box from '#mui/material/Box';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import Button from '#mui/material/Button';
import IconButton from '#mui/material/IconButton';
import MenuIcon from '#mui/icons-material/Menu';
import { useTheme, ThemeProvider, createTheme } from '#mui/material/styles';
import { teal } from '#mui/material/colors';
import { withStyles } from '#mui/styles';
import { Switch } from '#mui/material';
import Brightness4Icon from '#mui/icons-material/Brightness4';
import Brightness7Icon from '#mui/icons-material/Brightness7';
const label = { inputProps: { 'aria-label': 'Switch' } };
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
const theme = createTheme({
Navbar: {
primary: {
// Purple and green play nicely together.
main: teal[500],
},
secondary: {
// This is green.A700 as hex.
main: '#11cb5f',
},
},
});
const TealTextTypography = withStyles({
root: {
color: "#008080"
}
})(Typography);
function Navbar() {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>\
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static"
style={{ background: 'transparent', boxShadow: 'none'}}>
<Toolbar>
<IconButton
size="large"
edge="start"
aria-label="menu"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<TealTextTypography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Mentors
</TealTextTypography>
<TealTextTypography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Mentees
</TealTextTypography>
<Box
sx={{
display: 'flex',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'background.default',
color: 'text.primary',
borderRadius: 1,
p: 3,
}}
>
<IconButton sx={{ ml: 1 }} onClick={colorMode.toggleColorMode} color="inherit">
{ theme.palette.mode === 'dark' ? <Brightness7Icon /> : <Brightness4Icon />}
</IconButton>
</Box>
</Toolbar>
</AppBar>
</Box>
</ThemeProvider>
</ColorModeContext.Provider>
);
}
export default Navbar;
I am certain I am mixing up my const. and placing them in the wrong places. Although I am new to react and Mui I did manage to get it to work statically, however, the toggle is proving to be difficult.
This seem to work for me
App.js
import React from 'react';
import {
ThemeProvider,
createTheme,
responsiveFontSizes,
} from '#mui/material/styles';
import { deepmerge } from '#mui/utils';
import useMediaQuery from '#mui/material/useMediaQuery';
import { getDesignTokens, getThemedComponents } from 'theme/Theme';
import { ColorModeContext } from 'config/color-context';
export default function App() {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [mode, setMode] = React.useState();
React.useEffect(() => {
setMode(prefersDarkMode ? 'dark' : 'light');
}, [prefersDarkMode]);
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
},
}),
[]
);
let theme = React.useMemo(
() =>
createTheme(deepmerge(getDesignTokens(mode), getThemedComponents(mode))),
[mode]
);
theme = responsiveFontSizes(theme);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
...
</ThemeProvider>
</ColorModeContext.Provider>
);
}
Theme.js
import { amber, deepOrange, grey, blue, common } from '#mui/material/colors';
const palette = {
light: {
primary: {
main: '#34C0AC',
light: '#B1DED3',
dark: '#00765A',
},
},
};
export const getDesignTokens = (mode) => ({
palette: {
mode,
...(mode === 'light'
? {
primary: {
main: palette.light.primary.main,
light: palette.light.primary.light,
dark: palette.light.primary.dark,
},
divider: amber[200],
text: {
primary: grey[900],
secondary: grey[800],
},
}
: {
primary: deepOrange,
divider: deepOrange[700],
background: {
default: deepOrange[900],
paper: deepOrange[900],
},
text: {
primary: '#fff',
secondary: grey[500],
},
}),
},
typography: {
fontFamily: [
'Oswald',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
].join(','),
body1: {
fontFamily: 'Poppins, Arial, sans-serif',
},
},
});
export const getThemedComponents = (mode) => ({
components: {
...(mode === 'light'
? {
MuiAppBar: {
styleOverrides: {
colorPrimary: {
backgroundColor: grey[800],
},
},
},
MuiLink: {
variant: 'h3',
},
MuiButton: {
styleOverrides: {
root: {
borderRadius: 0,
color: common.white,
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
fontSize: 20,
borderWidth: 2,
'&:hover': {
borderWidth: 2,
},
},
},
variants: [
{
props: { variant: 'contained' },
style: {
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
},
{
props: { variant: 'outlined' },
style: {
color: palette.light.primary.main,
},
},
{
props: { variant: 'primary', color: 'primary' },
style: {
border: '4px dashed blue',
},
},
],
},
MuiList: {
styleOverrides: {
root: {},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
color: common.white,
alignItems: 'stretch',
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
},
},
MuiAccordion: {
styleOverrides: {
root: {
color: common.white,
fontFamily:
"Oswald, Roboto, 'Helvetica Neue', Arial, sans-serif",
},
},
},
}
: {
MuiAppBar: {
styleOverrides: {
colorPrimary: {
backgroundColor: blue[800],
},
},
},
}),
},
});
color-context.js
import React from 'react';
export const ColorModeContext = React.createContext({
toggleColorMode: () => {
// This is intentional
},
});
ThemeToggler.js
import React from 'react';
import { IconButton, Box } from '#mui/material';
import { useTheme } from '#mui/material/styles';
import Brightness4Icon from '#mui/icons-material/Brightness4';
import Brightness7Icon from '#mui/icons-material/Brightness7';
import { ColorModeContext } from 'config/color-context';
export default function SubHeaderNavigation() {
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
return (
<Box
sx={{
display: 'flex',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'background.default',
color: 'text.primary',
borderRadius: 1,
p: 3,
}}
>
{theme.palette.mode} mode
<IconButton
sx={{ ml: 1 }}
onClick={colorMode.toggleColorMode}
color="inherit"
>
{theme.palette.mode === 'dark' ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</Box>
);
}
Make sure you are not nesting your Themes or both could be negated.
For example:
App.tsx
<>
<ThemeProvider theme={!isDark ? lightTheme : darkTheme}>
<CssBaseline />
<Dashboard />
</ThemeProvider>
</>
Dashboard.tsx taken from here
function Dashboard() {
...
return (
<ThemeProvider theme={mdTheme}>
<Box sx={{ display: 'flex' }}>
<CssBaseline />
...
</ThemeProvider>
);
}
export default function Dashboard() {
return <DashboardContent />;
}
The effect of this will negate the Themes in the App.tsx config.

"TypeError: children.props is undefined" error when adding <div> to NextJS component return statement

React/NextJS newbie issue any help is much appreciated. I have a header component built on React/NextJS and Material. This return statement works like a charm.
return (
<header>
<HideOnScroll {...props}>
<AppBar className={header}>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</HideOnScroll>
</header>
);
But as soon as I add a basic div, as shown below, I get "TypeError: children.props is undefined". I'm just trying to add a full width div with a height of 10px to the top of the header for some trim but can't even get a div to render. I've tried adding {...props} to the div but still no luck.
return (
<header>
<HideOnScroll {...props}>
<div>test</div>
<AppBar className={header}>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</HideOnScroll>
</header>
);
Full source code below:
import {
AppBar,
Toolbar,
Typography,
makeStyles,
Button,
IconButton,
Drawer,
Link,
MenuItem,
Slide,
useScrollTrigger
} from "#material-ui/core";
import MenuIcon from "#material-ui/icons/Menu";
import React, { useState, useEffect } from "react";
import NextLink from "next/link";
const headersData = [
{
label: "Home",
href: "/",
},
{
label: "Blog",
href: "/blog/ryan-test",
},
];
const useStyles = makeStyles(() => ({
header: {
backgroundColor: "#400CCC",
paddingRight: "79px",
paddingLeft: "118px",
"#media (max-width: 900px)": {
paddingLeft: 0,
},
},
logo: {
fontFamily: "Work Sans, sans-serif",
fontWeight: 600,
color: "#FFFEFE",
textAlign: "left",
},
menuButton: {
fontFamily: "Open Sans, sans-serif",
fontWeight: 700,
size: "18px",
marginLeft: "38px",
},
toolbar: {
display: "flex",
justifyContent: "space-between",
},
drawerContainer: {
padding: "20px 30px",
},
topTrim: {
height: "10px",
width: "100%",
backgroundColor: "red",
}
}));
export default function Header(props) {
const { header, logo, menuButton, toolbar, drawerContainer, topTrim } = useStyles();
const [state, setState] = useState({
mobileView: false,
drawerOpen: false,
});
const { mobileView, drawerOpen } = state;
const TopTrim = () => {
return <div>Hello</div>;
}
useEffect(() => {
const setResponsiveness = () => {
return window.innerWidth < 900
? setState((prevState) => ({ ...prevState, mobileView: true }))
: setState((prevState) => ({ ...prevState, mobileView: false }));
};
setResponsiveness();
window.addEventListener("resize", () => setResponsiveness());
}, []);
const HideOnScroll = (props) => {
const { children } = props;
const trigger = useScrollTrigger();
return (
<Slide appear={false} direction="down" in={!trigger}>
{children}
</Slide>
);
}
const displayDesktop = () => {
return (
<Toolbar className={toolbar}>
{femmecubatorLogo}
<div>{getMenuButtons()}</div>
</Toolbar>
);
};
const displayMobile = () => {
const handleDrawerOpen = () =>
setState((prevState) => ({ ...prevState, drawerOpen: true }));
const handleDrawerClose = () =>
setState((prevState) => ({ ...prevState, drawerOpen: false }));
return (
<Toolbar>
<IconButton
{...{
edge: "start",
color: "inherit",
"aria-label": "menu",
"aria-haspopup": "true",
onClick: handleDrawerOpen
}}
>
<MenuIcon />
</IconButton>
<Drawer
{...{
anchor: "left",
open: drawerOpen,
onClose: handleDrawerClose,
}}
>
<div key="test1" className={drawerContainer}>{getDrawerChoices()}</div>
</Drawer>
<div key="test2">{femmecubatorLogo}</div>
</Toolbar>
);
};
const getDrawerChoices = () => {
return headersData.map(({ label, href }) => {
return (
<NextLink href={href} key={label} passHref>
<MenuItem>{label}</MenuItem>
</NextLink>
);
});
};
const femmecubatorLogo = (
<Typography variant="h6" component="h1" className={logo}>
Femmecubator
</Typography>
);
const getMenuButtons = () => {
return headersData.map(({ label, href }) => {
return (
<NextLink href={href} key={label} passHref>
<Button
{...{
key: label,
color: "inherit",
to: href,
className: menuButton,
}}
>
{label}
</Button>
</NextLink>
);
});
};
return (
<header>
<HideOnScroll {...props}>
<div>test</div>
<AppBar className={header}>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</HideOnScroll>
</header>
);
}
Pass it like this :
<HideOnScroll {...props}>
<>
<div>test</div>
<AppBar className={header}>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</>
</HideOnScroll>
Ultimately, I ended up in moving the div to inside of the AppBar. I now have to adjust AppBar padding and spacing of anything living inside of AppBar to compensate for the div also living there but it is working.
return (
<header>
<HideOnScroll {...props}>
<AppBar className={header}>
<div className={topTrim}>This is a test</div>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</HideOnScroll>
</header>
);

TypeError: theme is undefined - When trying to render Material UI component

I am having trouble rendering my react component since I separated my jss file and changed it from makeStyles to withStyles to avoid a hook problem.
I am getting an error message in my jss styling file as a couple of the methods have a 'theme' in the parenthesis for the styling to work off.
How do I go about changing this so that it renders correctly?
accessControl.component.js
import {connect, useSelector} from "react-redux";
import DataTable from "./userListTable.component";
import {Paper} from "#material-ui/core";
function AdminAccessControl(props) {
const { children, value, index, ...other } = props;
// select user object from redux
const user = useSelector(state => state.user);
// select school object from redux
const school = useSelector(state => state.diveSchool);
const classes = useStyles();
const [role, setRole] = useState({
userRole: "",
});
const handleChange = (property) => (e) => {
setRole({
// override the changed property and keep the rest
...role,
[property]: e.target.value,
});
}
return (
<div className={classes.root}>
<StyledTabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
aria-label="styled tabs example"
centered>
<StyledTab label="User Access Control" />
{/*<DataTable />*/}
<StyledTab label="Scuba School Access Control" />
{/*<DataTable />*/}
</StyledTabs>
<Typography className={classes.padding} />
</div>
);
}
function mapStateToProps(state){
const { user } = state.auth;
const { school } = state.diveSchool;
return {
user,
school,
};
}
export default compose(
connect(
mapStateToProps,
),
withStyles(useStyles)
)(AdminAccessControl);
myJss-style.js
export const useStyles = (theme) => ({
root: {
flexGrow: 1,
},
padding: {
padding: theme.spacing(3),
},
demo1: {
backgroundColor: theme.palette.background.paper,
},
demo2: {
backgroundColor: '#2e1534',
},
});
export const StyledTabs = () => ({
indicator: {
display: 'flex',
justifyContent: 'center',
backgroundColor: 'transparent',
'& > span': {
maxWidth: 40,
width: '100%',
backgroundColor: '#635ee7',
},
},
})((props) => <StyledTabs {...props} TabIndicatorProps={{ children: <span /> }} />);
export const StyledTab = (theme) => ({
root: {
textTransform: 'none',
color: '#fff',
fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.pxToRem(15),
marginRight: theme.spacing(1),
'&:focus': {
opacity: 1,
},
},
})((props) => <StyledTab disableRipple {...props} />);

React + Material UI: Static pages do not display

Background
I’m building my first react app and building out the basic layout (Header / Footer) and routes to the static pages (About, Contact, etc). My header shows up and when I click the links, the URL in the browser changes (/ -> /about).
Problem
The problem is the content within the pages does not show up.
What I’m doing
My Index.js imports my App. The App imports from the Header and Routes (defined in a Routes.js file). I have separate files for each page (About.js, Contact.js).
Question
Why isn't the body changing when I navigate to /about or /contact?
Code
App.js
import "./App.css";
import Header from "./components/Header";
import { BrowserRouter as Router } from "react-router-dom";
import Routes from "./Routes";
export default function App() {
return (
<Router>
<main>
<Header />
<Routes />
</main>
</Router>
);
Routes.js
export default function Routes() {
return (
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
);
}
Contact.js
import React, { Fragment } from "react";
export default function Contact() {
return (
<Fragment>
<h1>Contact</h1>
</Fragment>
);
}
Header.js
import {
AppBar,
Toolbar,
Typography,
makeStyles,
Button,
IconButton,
Drawer,
Link,
MenuItem,
} from "#material-ui/core";
import MenuIcon from "#material-ui/icons/Menu";
import React, { useState, useEffect } from "react";
import { Link as RouterLink } from "react-router-dom";
import headersData from "../data/header.json";
const useStyles = makeStyles(() => ({
header: {
backgroundColor: "#400CCC",
paddingRight: "79px",
paddingLeft: "118px",
"#media (max-width: 900px)": {
paddingLeft: 0,
},
},
logo: {
fontFamily: "Work Sans, sans-serif",
fontWeight: 600,
color: "#FFFEFE",
textAlign: "left",
},
menuButton: {
fontFamily: "Open Sans, sans-serif",
fontWeight: 700,
size: "18px",
marginLeft: "38px",
},
toolbar: {
display: "flex",
justifyContent: "space-between",
},
drawerContainer: {
padding: "20px 30px",
},
}));
export default function Header() {
const { header, logo, menuButton, toolbar, drawerContainer } = useStyles();
const [state, setState] = useState({
mobileView: false,
drawerOpen: false,
});
const { mobileView, drawerOpen } = state;
useEffect(() => {
const setResponsiveness = () => {
return window.innerWidth < 900
? setState((prevState) => ({ ...prevState, mobileView: true }))
: setState((prevState) => ({ ...prevState, mobileView: false }));
};
setResponsiveness();
window.addEventListener("resize", () => setResponsiveness());
}, []);
const getDrawerChoices = () => {
return headersData.map(({ label, href }) => {
return (
<Link
{...{
component: RouterLink,
to: href,
color: "inherit",
style: { textDecoration: "none" },
key: label,
}}
>
<MenuItem>{label}</MenuItem>
</Link>
);
});
};
const Logo = (
<Typography variant="h5" component="h1" className={logo}>
Boats
</Typography>
);
const getMenuButtons = () => {
return headersData.map(({ label, href }) => {
return (
<Button
{...{
key: label,
color: "inherit",
to: href,
component: RouterLink,
className: menuButton,
}}
>
{label}
</Button>
);
});
};
const displayDesktop = () => {
return (
<Toolbar className={toolbar}>
{Logo}
<div>{getMenuButtons()}</div>
</Toolbar>
);
};
const displayMobile = () => {
const handleDrawerOpen = () =>
setState((prevState) => ({ ...prevState, drawerOpen: true }));
const handleDrawerClose = () =>
setState((prevState) => ({ ...prevState, drawerOpen: false }));
return (
<Toolbar>
<IconButton
{...{
edge: "start",
color: "inherit",
"aria-label": "menu",
"aria-haspopup": "true",
onClick: handleDrawerOpen,
}}
>
<MenuIcon />
</IconButton>
<Drawer
{...{
anchor: "left",
open: drawerOpen,
onClose: handleDrawerClose,
}}
>
<div className={drawerContainer}>{getDrawerChoices()}</div>
</Drawer>
<div>{Logo}</div>
</Toolbar>
);
};
return (
<header>
<AppBar className={header}>
{mobileView ? displayMobile() : displayDesktop()}
</AppBar>
</header>
);
}

How can I create a button to a dialog box inside the PopperComponent of Material UI Labs Autocomplete

I have a Material UI Autocomplete which renders a list of Chips. I have added a button at the bottom of the PopperComponent which when clicked should open a dialog box.
But the Autocomplete doesn't allow the DialogBox to be opened. But the weird part is if I add 'open' to Autocomplete it works.
I have tried adding the onMouseDown event instead of onClick. Also, tried the event.preventDefault(). None of them works. However onMouseDown definitely called my listener for the Dialog box and changed its open state to true, but the dialog box did not appear.
This is the link to the sandbox.
Sandbox to the code
This is the component that implements the Dialog Box.
import React, { useState } from "react";
import { withStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import MuiDialogTitle from "#material-ui/core/DialogTitle";
import MuiDialogContent from "#material-ui/core/DialogContent";
import MuiDialogActions from "#material-ui/core/DialogActions";
import IconButton from "#material-ui/core/IconButton";
import CloseIcon from "#material-ui/icons/Close";
import Typography from "#material-ui/core/Typography";
import { orange } from "#material-ui/core/colors";
const styles = theme => ({
form: {
display: "flex",
flexDirection: "column",
margin: "auto",
width: "fit-content"
},
formControl: {
marginTop: theme.spacing(2),
minWidth: 120
},
formControlLabel: {
marginTop: theme.spacing(1)
},
closeButton: {
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500]
},
selectEmpty: {
marginTop: theme.spacing(2)
},
floatingLabelFocusStyle: {
color: "green"
},
separator: {
marginTop: theme.spacing(1)
},
menuStyle: {
border: "1px solid black",
borderRadius: "5%",
backgroundColor: "lightgrey"
}
});
const DialogTitle = withStyles(styles)(props => {
const { children, classes, onClose, ...other } = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton
aria-label="close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}{" "}
</MuiDialogTitle>
);
});
const DialogContent = withStyles(theme => ({
root: {
padding: theme.spacing(2)
}
}))(MuiDialogContent);
const DialogActions = withStyles(theme => ({
root: {
margin: 0,
padding: theme.spacing(1)
}
}))(MuiDialogActions);
const ActionButton = withStyles(theme => ({
root: {
color: "#E87424",
backgroundColor: "white",
"&:hover": {
backgroundColor: orange[100]
}
}
}))(Button);
const ManageTagButton = withStyles(theme => ({
root: {
color: "#E87424"
}
}))(Button);
const TagContainer = props => {
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
console.log("Dialog box clicked");
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<ManageTagButton
onMouseDown={event => {
event.preventDefault();
handleClickOpen();
}}
size="small"
>
MANAGE TAGS
</ManageTagButton>
<Dialog
fullWidth
maxWidth={"sm"}
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
>
<DialogTitle id="customized-dialog-title">Manage Tags</DialogTitle>
<DialogContent dividers>
<h1>
This is the component of the dialog box. In reality I neeed to
display a data table with CRUD operations to add more tags.
</h1>
</DialogContent>
<DialogActions>
<ActionButton autoFocus onClick={handleClose} color="secondary">
CLOSE
</ActionButton>
</DialogActions>
</Dialog>
</div>
);
};
export default TagContainer;
This is the component that implements the Autocomplete.
import React, { Fragment } from "react";
import Chip from "#material-ui/core/Chip";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { withStyles } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import ListItemSecondaryAction from "#material-ui/core/ListItemSecondaryAction";
import Paper from "#material-ui/core/Paper";
import TagContainer from "./TagContainer";
const ListItemCustom = withStyles(theme => ({
gutters: {
paddingLeft: 0,
paddingRight: 0
},
secondaryAction: {
paddingRight: 0
}
}))(ListItem);
const AutocompleteCustom = withStyles(theme => ({
endAdornment: {
display: "none"
}
}))(Autocomplete);
const CreateButton = withStyles(theme => ({
root: {
color: "#E87424"
}
}))(Button);
const MuiFilledInputCustom = makeStyles(
{
underline: {
"&&&:before": {
borderBottom: "none"
},
"&&:after": {
borderBottom: "none"
}
}
},
{ name: "MuiFilledInput" }
);
const loadCustomStyles = () => {
MuiFilledInputCustom();
};
export default function AddTagToThread() {
loadCustomStyles();
const handleSubmit = () => {
console.log("Add tags to thread");
};
const useStyles = makeStyles({
root: {
minWidth: 300,
width: 300,
height: 250,
minHeight: 250,
zIndex: 1
},
buttons: {
display: "flex",
justifyContent: "flex-end"
}
});
const PaperComponentCustom = options => {
const classes = useStyles();
const { containerProps, children } = options;
return (
<Paper className={classes.root} {...containerProps} square>
{children}
<div className={classes.buttons}>
<TagContainer />
</div>
</Paper>
);
};
return (
<List dense={false}>
<ListItemCustom>
<ListItemText>
<AutocompleteCustom
multiple
id="size-small-filled-multi"
size="medium"
options={tagList}
noOptionsText="No options"
freeSolo
filterSelectedOptions
PaperComponent={PaperComponentCustom}
getOptionLabel={option => option.name}
onChange={(event, value) => {
console.log(value);
}}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="default"
style={{
backgroundColor: option.color
}}
label={option.name}
size="medium"
{...getTagProps({ index })}
/>
))
}
renderOption={option => (
<Fragment>
<Chip
variant="default"
style={{
backgroundColor: option.color,
padding: "15px",
marginLeft: "12px"
}}
label={option.name}
size="medium"
/>
</Fragment>
)}
renderInput={params => (
<TextField
{...params}
variant="filled"
label="Filter By Tag"
placeholder="Select Tag"
/>
)}
/>
</ListItemText>
<ListItemSecondaryAction>
<CreateButton onClick={handleSubmit}>ADD TAG</CreateButton>
</ListItemSecondaryAction>
</ListItemCustom>
</List>
);
}
const tagList = [
{ name: "Follow Up", tagId: 1, color: "#FFC107" },
{ name: "Important", tagId: 2, color: "#46B978" },
{ name: "Idea", tagId: 3, color: "#EEA5F6" },
{ name: "Non Issue", tagId: 4, color: "#2EACE2" }
];
I have been stuck at this for last couple of days. Any help is greatly appreciated.
The issue with your code is that the <Dialog/> component is in the PaperComponentCustom component which gets unmounted after the option is selected.
<Paper className={classes.root} {...containerProps} square>
{children}
<ManageTagButton onMouseDown={handleClickOpen} fullWidth>
MANAGE TAGS
</ManageTagButton>
</Paper>
The solution to keep only the <ManageTagButton/> component in the PaperComponentCustom and move the <Dialog/> component one level up. I imagine that even if you have 10 elements in the <List/> you would still have only one <Dialog>, you cannot have 10 dialog components opened at once.
So therefore your <AddTagToThread/> component should render the dialog directly and the state of the dialog open and the handlers handleOpen and handleClose should be moved in the <AddTagToThread/> component also
Working codesandbox HERE, code below
Autocomplete component
import React, { Fragment, useState } from "react";
import Chip from "#material-ui/core/Chip";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { withStyles } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import ListItemSecondaryAction from "#material-ui/core/ListItemSecondaryAction";
import Paper from "#material-ui/core/Paper";
import TagContainer from "./TagContainer";
const ListItemCustom = withStyles(theme => ({
gutters: {
paddingLeft: 0,
paddingRight: 0
},
secondaryAction: {
paddingRight: 0
}
}))(ListItem);
const AutocompleteCustom = withStyles(theme => ({
endAdornment: {
display: "none"
}
}))(Autocomplete);
const CreateButton = withStyles(theme => ({
root: {
color: "#E87424"
}
}))(Button);
const MuiFilledInputCustom = makeStyles(
{
underline: {
"&&&:before": {
borderBottom: "none"
},
"&&:after": {
borderBottom: "none"
}
}
},
{ name: "MuiFilledInput" }
);
const loadCustomStyles = () => {
MuiFilledInputCustom();
};
const ManageTagButton = withStyles(theme => ({
root: {
color: "#E87424"
}
}))(Button);
export default function AddTagToThread() {
loadCustomStyles();
const handleSubmit = () => {
console.log("Add tags to thread");
};
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
console.log("Dialog box clicked");
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const useStyles = makeStyles({
root: {
minWidth: 300,
width: 300,
height: 250,
minHeight: 250,
zIndex: 1
},
buttons: {
display: "flex",
justifyContent: "flex-end"
}
});
const PaperComponentCustom = options => {
const classes = useStyles();
const { containerProps, children } = options;
return (
<Paper className={classes.root} {...containerProps} square>
{children}
<ManageTagButton onMouseDown={handleClickOpen} fullWidth>
MANAGE TAGS
</ManageTagButton>
</Paper>
);
};
return (
<>
<TagContainer open={open} handleClose={handleClose} />
<List dense={false}>
<ListItemCustom>
<ListItemText>
<AutocompleteCustom
multiple
id="size-small-filled-multi"
size="medium"
options={tagList}
noOptionsText="No options"
freeSolo
filterSelectedOptions
PaperComponent={PaperComponentCustom}
getOptionLabel={option => option.name}
onChange={(event, value) => {
console.log(value);
}}
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="default"
style={{
backgroundColor: option.color
}}
label={option.name}
size="medium"
{...getTagProps({ index })}
/>
))
}
renderOption={option => (
<Fragment>
<Chip
variant="default"
style={{
backgroundColor: option.color,
padding: "15px",
marginLeft: "12px"
}}
label={option.name}
size="medium"
/>
</Fragment>
)}
renderInput={params => (
<TextField
{...params}
variant="filled"
label="Filter By Tag"
placeholder="Select Tag"
/>
)}
/>
</ListItemText>
<ListItemSecondaryAction>
<CreateButton onClick={handleSubmit}>ADD TAG</CreateButton>
</ListItemSecondaryAction>
</ListItemCustom>
</List>
</>
);
}
const tagList = [
{ name: "Follow Up", tagId: 1, color: "#FFC107" },
{ name: "Important", tagId: 2, color: "#46B978" },
{ name: "Idea", tagId: 3, color: "#EEA5F6" },
{ name: "Non Issue", tagId: 4, color: "#2EACE2" }
];
Dialog component
import React, { useState } from "react";
import { withStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import MuiDialogTitle from "#material-ui/core/DialogTitle";
import MuiDialogContent from "#material-ui/core/DialogContent";
import MuiDialogActions from "#material-ui/core/DialogActions";
import IconButton from "#material-ui/core/IconButton";
import CloseIcon from "#material-ui/icons/Close";
import Typography from "#material-ui/core/Typography";
import { orange } from "#material-ui/core/colors";
const styles = theme => ({
form: {
display: "flex",
flexDirection: "column",
margin: "auto",
width: "fit-content"
},
formControl: {
marginTop: theme.spacing(2),
minWidth: 120
},
formControlLabel: {
marginTop: theme.spacing(1)
},
closeButton: {
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500]
},
selectEmpty: {
marginTop: theme.spacing(2)
},
floatingLabelFocusStyle: {
color: "green"
},
separator: {
marginTop: theme.spacing(1)
},
menuStyle: {
border: "1px solid black",
borderRadius: "5%",
backgroundColor: "lightgrey"
}
});
const DialogTitle = withStyles(styles)(props => {
const { children, classes, onClose, ...other } = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton
aria-label="close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}{" "}
</MuiDialogTitle>
);
});
const DialogContent = withStyles(theme => ({
root: {
padding: theme.spacing(2)
}
}))(MuiDialogContent);
const DialogActions = withStyles(theme => ({
root: {
margin: 0,
padding: theme.spacing(1)
}
}))(MuiDialogActions);
const ActionButton = withStyles(theme => ({
root: {
color: "#E87424",
backgroundColor: "white",
"&:hover": {
backgroundColor: orange[100]
}
}
}))(Button);
const TagContainer = ({ open, handleClose }) => {
return (
<Dialog
fullWidth
maxWidth={"sm"}
onClose={handleClose}
aria-labelledby="customized-dialog-title"
open={open}
>
<DialogTitle id="customized-dialog-title">Manage Tags</DialogTitle>
<DialogContent dividers>
<h1>
This is the component of the dialog box. In reality I neeed to display
a data table with CRUD operations to add more tags.
</h1>
</DialogContent>
<DialogActions>
<ActionButton autoFocus onClick={handleClose} color="secondary">
CLOSE
</ActionButton>
</DialogActions>
</Dialog>
);
};
export default TagContainer;

Resources