How to apply styles to MUI MenuList with styled compoents - reactjs

I'm trying to apply background-color when MenuItem component has the selected={true}
also I'd like to apply a style when MenuItem component hovered.
How can I do that?
import * as React from "react";
import Paper from "#mui/material/Paper";
import MenuList from "#mui/material/MenuList";
import Stack from "#mui/material/Stack";
import { MenuItem } from "./styles";
export default function MenuListComposition() {
return (
<Stack direction="row" spacing={2}>
<Paper>
<MenuList>
<MenuItem selected={true}>Profile</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem>Logout</MenuItem>
</MenuList>
</Paper>
</Stack>
);
}
styles.js
import styled from "styled-components";
import { default as MuiMenuItem } from "#mui/material/MenuItem";
export const MenuItem = styled(MuiMenuItem)`
.MuiMenuItem-root {
color: blue;
padding: 10px 0;
& .Mui-selected {
background-color: red;
}
&:hover {
background-color: green;
}
}
`;
Solution with styled-components
If you need to use styled-components instead of styled from Mui, you can do it.
export const MenuItem = styled(MuiMenuItem)`
color: blue;
padding: 20px;
&.Mui-selected {
background-color: red;
}
&:hover {
background-color: green;
}
`;

You can fix the problem by importing styled from material and adapt the format, like this:
import { MenuItem as MuiMenuItem, styled } from "#mui/material";
export const MenuItem = styled(MuiMenuItem)({
"&.MuiMenuItem-root": {
color: "blue",
padding: "10px",
"&.Mui-selected": {
backgroundColor: "red"
},
"&:hover": {
backgroundColor: "green"
}
}
});
and then I assume that what you want is to change the color only of the selected MenuItem and you can create the array of objects like in the code so you do not repeat to much code.
import React, { useState } from "react";
import Paper from "#mui/material/Paper";
import MenuList from "#mui/material/MenuList";
import Stack from "#mui/material/Stack";
import { MenuItem } from "./styles";
const ITEMS = [
{ index: 1, title: "Profile" },
{ index: 2, title: "My Account" },
{ index: 3, title: "Logout" }
];
export default function MenuListComposition() {
const [selectedIndex, setSelectedIndex] = useState(1);
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};
return (
<Stack direction="row" spacing={2}>
<Paper>
<MenuList variant="menu">
{ITEMS.map(({ index, title }) => (
<MenuItem
key={index}
onClick={(event) => handleListItemClick(event, index)}
selected={selectedIndex === index}
>
{title}
</MenuItem>
))}
</MenuList>
</Paper>
</Stack>
);
}

You need to make some configuration changes, because MUI uses 'emotion' as a default style engine, otherwise they would conflict with each other.
https://mui.com/material-ui/guides/styled-engine/#how-to-switch-to-styled-components
here is a working example you need to compare dependencies that are recommended for usage with react

Related

Apply styling from parent component to a specific component in child component | react, material ui, styled-components

There are multiple components in child component and can I apply styling to a specific component that is in child component from parent component?
App/index.jsx
import React from "react";
import { Container, StyledMenu } from "./styles";
const App = ({ className }) => {
return (
<Container className={className}>
<h1>this is a demo</h1>
<StyledMenu />
</Container>
);
};
export default App;
App/styles.js
import styled from "styled-components";
import Demo from "../Demo";
export const Container = styled.div`
background-color: pink;
padding: 20px;
`;
Container.displayName = "Container";
export const StyledMenu = styled(Demo)`
& .MuiPaper-root {
width: 300px;
}
`;
StyledMenu.displayName = "StyledMenu";
Demo/index.jsx
import * as React from "react";
import Button from "#mui/material/Button";
// import Menu from "#mui/material/Menu";
import MenuItem from "#mui/material/MenuItem";
import { MenuDisplay } from "./styles";
export default function BasicMenu({ className }) {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div className={className}>
<Button
id="basic-button"
aria-controls={open ? "basic-menu" : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
onClick={handleClick}
>
Dashboard
</Button>
<MenuDisplay
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": "basic-button"
}}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</MenuDisplay>
</div>
);
}
Demo/styles.js
import styled from "styled-components";
import Menu from "#mui/material/Menu";
export const MenuDisplay = styled(Menu)`
background-color: green;
`;
MenuDisplay.displayName = "MenuDisplay";
I like to apply width: 300px for the Menu component (where it is a dropdown menu) in Demo/index.jsx from App/style.js.
Is that possible?
Here is codesandbox for demo
Here is Menu component from mui doc
Attempts
I imported MenuDisplay (that's in Demo/styles.js) inside App/styles.js
import styled from "styled-components";
import Demo from "../Demo";
import { MenuDisplay } from "../Demo/styles";
export const Container = styled.div`
background-color: pink;
padding: 20px;
`;
Container.displayName = "Container";
export const StyledMenu = styled(Demo)`
${MenuDisplay} & {
& .MuiPaper-root {
width: 300px;
}
}
`;
StyledMenu.displayName = "StyledMenu";
This didn't work
To apply css to your child component, define width in the Demo/styles.js
export const MenuDisplay = styled(Menu)width: 300px;;
For more detail see the replay https://dashboard.testwise.io/replays/bRJwLQfxEcG of your issue.

What is the proper way of using the styling defined inside createMuiTheme alongside with Material-UI's useStyles?

import React from 'react';
import Component from './components/Component';
import { createMuiTheme, makeStyles, ThemeProvider } from '#material-ui/core/styles';;
const theme = createMuiTheme({
container: {
width: '100%',
paddingRight: '15px',
paddingLeft: '15px',
marginRight: 'auto',
marginLeft: 'auto'
},
flexColumn: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}
});
function App() {
return (
<ThemeProvider theme={theme}>
<div className="App">
<Component />
</div>
</ThemeProvider>
);
}
export default App;
The theme provided above goes inside the component Component.
import React, { useState } from "react";
import { makeStyles } from '#material-ui/core/styles';
import { useTheme } from '#material-ui/core/styles';
import classNames from 'classnames';
const useStyles = makeStyles(() => ({
bar: {
flexGrow: 1,
padding: '.8rem .8rem',
lineHeight: '1.5em',
fontSize: '18px',
},
alert: {
color: 'white',
backgroundColor: 'red',
},
}));
export default function Component (props) {
const theme = useTheme();
const classes = useStyles();
const [focus, setFocus] = useState(false);
return (
<div>
<div style={theme.flexColumn} className={classNames(classes.alert, classes.bar)}>
<div>
</div>
</div>
</div>
);
}
Is this the proper way to use useTheme and className? The issue with this is that I can't use the styles defined with createMuiTheme and fetched through the ThemeProvider inside the className attribute, which means sometimes the styling inside classNames and style may conflict with one another. I couldn't find an example where the styling provided inside createMuiTheme is used with className.
If you want to reuse classes in MUI, you should create an external style file, then import this file into the components that you want to use this style. Try this:
In sharedStyles:
export const layout = {
header: {
fontSize: "1.5em",
color: "blue"
},
footer: {
fontSize: "0.8em"
}
};
const sharedStyles = theme => ({
grow: {
flexGrow: 1
},
defaultPadding: {
padding: theme.spacing.unit * 3 + "px"
},
"layout.header": layout.header
});
export default sharedStyles;
In a component:
import React from "react";
import { withStyles } from "#material-ui/core/styles";
import Paper from "#material-ui/core/Paper";
import sharedStyles, { layout } from "./sharedStyles";
import Typography from "#material-ui/core/Typography";
function Dashboard(props) {
const { classes } = props;
return (
<Paper className={classes.defaultPadding}>
<Typography variant="body1" gutterBottom>
Hello <span className={classes.someOtherStyle}>World</span>
</Typography>
<Typography
component="h2"
variant="h1"
gutterBottom
className={classes["layout.header"]}
>
Heading
</Typography>
<Typography
component="h2"
variant="h1"
gutterBottom
className={classes.header}
>
Heading 2
</Typography>
</Paper>
);
}
const styles = theme => ({
...sharedStyles(theme),
header: layout.header,
someOtherStyle: {
color: "red"
}
});
export default withStyles(styles)(Dashboard);

material-ui how to override nested styles

I have a simple Material-UI component
<Chip
avatar={
<Avatar>
<TruckIcon color='primary' />
</Avatar>
}
label={name}
color='primary'
/>
the following styles applied from mui-theme:
.MuiChip-root .MuiChip-avatarColorPrimary {
color: #fff;
background-color: #21349f;
}
The question is how to override it?
I have tried many options in my theme override styles:
MuiChip: {
avatarColorPrimary : {
background-color: red;
}
MuiChip: {
root: {
avatarColorPrimary : {
background-color: red;
}
}
}
MuiChip: {
'& .avatarColorPrimary': {
background-color: red;
}
but none of them is working.
I'm using 4.9.5 version of Material-UI.
The most useful resource when trying to determine the appropriate way to override a style is to look at how the default styles are defined in the source code.
Below is an excerpt of the default styles from the Chip source code:
{
root: {
'& $avatarColorPrimary': {
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.dark,
}
}
}
Below is an example of overriding the background color in the theme:
import React from "react";
import { createMuiTheme, ThemeProvider } from "#material-ui/core/styles";
import Avatar from "#material-ui/core/Avatar";
import Chip from "#material-ui/core/Chip";
import DoneIcon from "#material-ui/icons/Done";
const theme = createMuiTheme({
overrides: {
MuiChip: {
root: {
"& $avatarColorPrimary": {
backgroundColor: "red"
}
}
}
}
});
export default function Chips() {
return (
<ThemeProvider theme={theme}>
<Chip
avatar={
<Avatar>
<DoneIcon color="primary" />
</Avatar>
}
label="Sample Name"
color="primary"
/>
</ThemeProvider>
);
}

Custom color to Badge component not working

I need to add a custom color to my Badge component and it does not seem to work.
I have tried these:
<Badge className="badge" badgeStyle={{backgroundColor: '#00AFD7'}} variant="dot" />
<Badge className="badge" color='#00AFD7' variant="dot" />
These do not work. How can I pass a custom color to my Badge component
You can leverage withStyles and use the badge css class to customize this.
Here's an example:
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import Badge from "#material-ui/core/Badge";
import MailIcon from "#material-ui/icons/Mail";
const styles = theme => ({
margin: {
margin: theme.spacing.unit * 2
},
customBadge: {
backgroundColor: "#00AFD7",
color: "white"
}
});
function SimpleBadge(props) {
const { classes } = props;
return (
<div>
<Badge
classes={{ badge: classes.customBadge }}
className={classes.margin}
badgeContent={10}
>
<MailIcon />
</Badge>
</div>
);
}
SimpleBadge.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(SimpleBadge);
In v4, you can use functions within the styles that leverage props.
Documentation here: https://material-ui.com/styles/basics/#adapting-the-higher-order-component-api
const styles = theme => ({
margin: {
margin: theme.spacing.unit * 2
},
customBadge: {
backgroundColor: props => props.color,
color: "white"
}
});
In MUI v5, you can use either the sx prop:
<Badge
badgeContent={130}
sx={{
"& .MuiBadge-badge": {
color: "lightgreen",
backgroundColor: "green"
}
}}
>
<MailIcon />
</Badge>
Or styled() function:
const StyledBadge = styled(Badge)({
"& .MuiBadge-badge": {
color: "red",
backgroundColor: "pink"
}
});
I left the bg property empty and it accepted the background from the style. I am using bootstrap-5.
<Badge bg="" style={{backgroundColor: '#00AFD7'}} variant="dot">...</Badge>

Reference to theme's primary color instead of a specific color in MUI

Using ReactJS and MUI, I have a project in which I have changed the theme colors.
const newTheme = getMuiTheme({
fontFamily: 'Roboto, sans-serif',
palette: {
primary1Color: cyan500,
primary2Color: cyan700,
primary3Color: grey400,
accent1Color: amberA400,
accent2Color: grey100,
accent3Color: grey500,
textColor: darkBlack,
alternateTextColor: white,
canvasColor: white,
borderColor: grey300,
disabledColor: fade(darkBlack, 0.3),
pickerHeaderColor: cyan500,
clockCircleColor: fade(darkBlack, 0.07),
shadowColor: fullBlack,
},
});
// App class
render() {
return(
<ThemeProvider theme={newTheme}>
<Project />
</ThemeProvider>
)
}
Everything works as expected. Certain components, like buttons have the ability to set the color based on the primary prop. However, I have a component that uses an icon that needs the primary color. I can import the color and set it directly:
import React from 'react';
import ActionLock from 'material-ui/svg-icons/action/lock';
import {cyan500} from 'material-ui/styles/colors';
export default class LockIcon extends React.Component {
render() {
return(
<ActionLock color={cyan500} />
)
}
}
Is there some way to reference the theme's primary color, rather than setting the color in each component individually? Something like:
import React from 'react';
import ActionLock from 'material-ui/svg-icons/action/lock';
import {primary1Color} from 'somewhere';
export default class LockIcon extends React.Component {
render() {
return(
<ActionLock color={primary1Color} />
)
}
}
If you're using React v16.8.0 and Material-UI v3.5.0 or greater, you can utilize the useTheme() hook:
import { useTheme } from '#material-ui/core/styles';
function LockIcon = () => {
const theme = useTheme();
return (
<ActionLock color={theme.palette.primary1Color} />
}
Yes you have!
using muiThemeable..
import muiThemeable from 'material-ui/styles/muiThemeable';
class LockIcon extends React.Component {
render() {
return(
<ActionLock color={this.props.muiTheme.palette.primary1Color} />
)
}
}
export default muiThemeable()(LockIcon)
from material-ui docs
Adding how to access theme colors in material-ui v1.0.0 (currently beta) Using withTheme component.
Also check the following Example.
import React, {Component} from 'react';
import { withTheme } from 'material-ui/styles';
class WithThemeExample extends Component {
render() {
const { theme } = props;
const {primary, secondary} = theme.palette.text;
return (
<div>
<div style={{color: primary}}>Hi in Primary color</div>
<div style={{color: secondary}}>Bye in Secondary color</div>
</div>
);
}
}
export default withTheme()(WithThemeExample);
If you're using system properties, you can define a string path of the Palette object to the color value like below:
<Box sx={{ color: "primary.main" }}>primary.main</Box>
<Box sx={{ color: "secondary.main" }}>secondary.main</Box>
<Box sx={{ color: "error.main" }}>error.main</Box>
<Box sx={{ color: "warning.main" }}>warning.main</Box>
<Box sx={{ color: "info.main" }}>info.main</Box>
<Box sx={{ color: "success.main" }}>success.main</Box>
<Box sx={{ color: "text.primary" }}>text.primary</Box>
<Box sx={{ color: "text.secondary" }}>text.secondary</Box>
<Box sx={{ color: "text.disabled" }}>text.disabled</Box>
The above is the same as:
<Box sx={{ color: theme => theme.palette.primary.main }}>primary.main</Box>
<Box sx={{ color: theme => theme.palette.secondary.main }}>secondary.main</Box>
<Box sx={{ color: theme => theme.palette.error.main }}>error.main</Box>
<Box sx={{ color: theme => theme.palette.warning.main }}>warning.main</Box>
<Box sx={{ color: theme => theme.palette.info.main }}>info.main</Box>
<Box sx={{ color: theme => theme.palette.success.main }}>success.main</Box>
<Box sx={{ color: theme => theme.palette.text.primary }}>text.primary</Box>
<Box sx={{ color: theme => theme.palette.text.secondary }}>text.secondary</Box>
<Box sx={{ color: theme => theme.palette.text.disabled }}>text.disabled</Box>
The example using the callback is more verbose but is type-safe, the shorter one with only string is quicker and good when prototyping, but you may want to store the string in a constant to prevent any typo errors and help the IDE refactor your code better.
Live Demo
Ok if you are using material-ui version greater than 4, then the above solution might not work for you. Follow the below's code
import { withTheme } from '#material-ui/core/styles';
function DeepChildRaw(props) {
return <span>{`spacing ${props.theme.spacing}`}</span>;
}
const DeepChild = withTheme(DeepChildRaw);
Reference: https://material-ui.com/styles/advanced/
With react hooks, now you don't need to warp component in withTheme, just use useTheme:
import { useTheme } from '#material-ui/core/styles';
export default function MyComponent() {
const theme = useTheme();
return <span>{`spacing ${theme.spacing}`}</span>;
}
MUI V5
For the users of MUI v5, you can specify a function inside the sx css style directly, ex:
<Box sx={{ color: (theme) => theme.palette.primary1Color }} />
You can use the useTheme() hook and access the colors like the example below.
There are also some other color variants as well.
Mui 5:
import * as React from 'react';
import PropTypes from 'prop-types';
import { NavLink } from "react-router-dom";
import { useTheme } from "#mui/material/styles";
function VinNavLink(props) {
const theme = useTheme();
return (
<NavLink {...props} style={({ isActive }) => isActive ? { textDecoration: "underline", color: theme.palette.primary.main} : undefined}>{props.children}</NavLink>
);
}
export default VinNavLink;

Resources