Change Material-UI ListItem children on hover/active - reactjs

Consider the following component structure of a side-bar navigation:
<ListItem button dense component={CustomNavLink} to="/dashboard">
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
The task is to change the ListItemIcon and ListItemText appearance on hover or when the CustomNavLink becomes active.
Note that CustomNavLink is an extended React Router's NavLink component
that gets an active class applied to when it matches with the current route.
The following, somewhat hacky way achieves that (abridged and simplified for clarity) via classes property:
const styles = {
root: {
...
'&.active, &:hover, &.active:hover': {
'& path': {
fill: 'red'
},
'& span': {
color: 'red'
}
}
}
};
(classes are then applied to the ListItem component)
This seems like an extremely lousy way of going about it, as the structure of the nested components leaks into the parent's styling... which is akin to doing this in the "old" CSS:
div:hover > ul > li > a {
color: red;
}
What is the idiomatic Material-UI way of solving this?
For reference, this is how it would be done in styled-components:
const CustomNavLink = styled(NavLink)`
...
&:hover {
${ListItemIcon} {
path: {
fill: red;
}
}
${ListItemText} {
color: red;
}
}
`;

Please Try following example for Change Material UI ListItem children on hover/active
const wrapperStyles = {
parent: {
backgroundColor: 'yellow',
'&:hover $child': {
color: 'red'
}
},
child: {
fontSize: '2em',
padding: 24
}
}

Expanding answer by #Patel Charul. In case you want to change the style of multiple children on hover.
const wrapperStyles = {
parent: {
backgroundColor: 'yellow',
'&:hover': {
'& $child1': {
color: 'red'
},
'& $child2': {
color: 'blue'
}
},
child1: {
fontSize: '2em',
padding: 24
},
child2: {
fontSize: '4em',
padding: 28
}
}

This is one way to do it in MUI v5, first import the following components:
import ListItem, { listItemClasses } from "#mui/material/ListItem";
import ListItemButton from "#mui/material/ListItemButton";
import ListItemIcon from "#mui/material/ListItemIcon";
import ListItemText from "#mui/material/ListItemText";
Then define the your custom NavLink, this NavLink will add the active className when it matches the current route:
function MyNavLink(props) {
return <NavLink {...props} activeClassName="active" />;
}
Then in the List component, add the following styles to style the ListItem in active and hover state:
<List
sx={{
[`& .active, & .${listItemClasses.root}:hover`]: {
color: "red",
fontWeight: "bold",
"& svg": {
fill: "red"
}
}
}}
>
<ListItem component={MyNavLink} to="/" exact>
<ListItemButton>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Home" />
</ListItemButton>
</ListItem>
{/* other NavLink component */}
</List>
Live Demo

Related

How can use Material ui custom pagination?

<Pagination count={35} variant="outlined" shape="rounded" color="primary"/>
This sets a default background colour and text colour, but I need custom background colour and text colour. I can't change it. I am trying to change the colour but I failed.
import * as React from 'react';
import Pagination from '#mui/material/Pagination';
import Stack from '#mui/material/Stack';
import { styled } from '#mui/system';
//Make a styled pagination component using styled API that overrides default style
const MyPagination = styled(Pagination)({
'& .Mui-selected': {
backgroundColor: 'red',
color:'#19D5C6',
},
"& .MuiPaginationItem-root": {
border: "1px solid red"
}
//There has a lot of Global classes. you have to use it according to your requirement
//Some classes are MuiPaginationItem-root, MuiPaginationItem-page and Mui-selected
});
export default function BasicPagination() {
return (
<Stack spacing={2}>
<MyPagination count={10} />
</Stack>
);
}
click to get all classes name
I would use a theme which will apply consistency throughout your project.
Codesandbox implementation here: https://codesandbox.io/s/mutable-river-e15yxw
Theme.js
import { ThemeProvider, createTheme } from "#mui/material/styles";
import CssBaseline from "#mui/material/CssBaseline";
export default function Theme(props) {
const theme = createTheme({
components: {
MuiPagination: {
styleOverrides: {
root: ({ ownerState }) => ({ backgroundColor: "#ffeeaa" })
}
},
MuiPaginationItem: {
styleOverrides: {
root: ({ ownerState }) => ({ backgroundColor: "#ffcc00", color: "maroon" })
}
}
}
});
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{props.children}
</ThemeProvider>
);
}
Demo.js
import * as React from "react";
import Pagination from "#mui/material/Pagination";
import Stack from "#mui/material/Stack";
import Theme from "./Theme";
export default function BasicPagination() {
return (
<Theme>
<Stack spacing={2}>
<Pagination count={10} sx={{ backgroundColor: "#eeffaa" }} />
<Pagination count={10} color="primary" />
<Pagination count={10} color="secondary" />
<Pagination count={10} disabled />
</Stack>
</Theme>
);
}
And there is another one if you prefer it, utilising the sx prop:
<Pagination
count={35}
shape="rounded"
color="primary"
sx={{
"& .MuiPagination-ul": { backgroundColor: "yellow" },
"& .MuiPaginationItem-page": { color: "red" },
"& .Mui-selected": { backgroundColor: "green" },
}}
/>
This is the result:
EDIT:
<Pagination
count={35}
shape="rounded"
color="primary"
sx={{
"& .MuiPagination-ul": { backgroundColor: "yellow" },
"& .MuiPaginationItem-page": { color: "red", border: "1px solid green" },
"& .Mui-selected": { backgroundColor: "green" },
}}
/>

Material UI makeStyles override class properties in it when it is under other class

I am using NavLink in react-router-dom with activeClassName. In that NavLink I have ListItems from material ui which has some css. I want to change that css if that particular NavLink has active class assigned.
I know I can't reference abc as "& abc" because makeStyle will create dynamic name for that. That's where I am stuck. Can anyone please help me to solve this problem?
This is the code I am trying:
const useStyles = makeStyles((theme) => ({
abc: {
color: "white",
},
xyz: {
"& abc": {
color: "red"
}
}
}
<List>
{pages.map((key) => {
let Component = key.icon;
return (
<NavLink
activeClassName={classes.xyz}
to={key.to}
onClick={() => setActive(key.to)}
>
<ListItem className={classes.abc}>
<ListItemIcon className={classes.abc}>
<Component />
</ListItemIcon>
<ListItemText
primary={key.label}
className="navText"
></ListItemText>
</ListItem>
</NavLink>
);
})}
</List>
Thanks in advance.
Using $ solved my problem.
const useStyles = makeStyles((theme) => ({
abc: {
color: "white",
},
xyz: {
"& $abc": {
color: "red"
}
}
}
Got this solution from documentation. Thank you!
abc is class so you need to add .
xyz: {
"& .abc": {
color: "red"
}
}
And change className={classes.abc} inside xyz to className="abc"

How to bolden ListItem text in Material UI React

I have the following styling to embolden the fontWeight of ListItem text in the Material UI library but unfortunately, it isn't working as expected.
Here is the code
import {
IconButton,
Collapse,
List,
ListItem, ListItemIcon,
ListItemText,
makeStyles,
useTheme
} from '#material-ui/core';
......
const useNavStyles = makeStyles((theme) => ({
navLinkItem: {
borderTopRightRadius: 100,
borderBottomRightRadius: 100,
paddingBottom: 12,
paddingTop: 12,
backgroundColor: theme.palette.background.paper,
fontWeight: 'bold' //This is not working
},
navLinkIcons: {
paddingLeft: 20,
},
nested: {
paddingLeft: theme.spacing(4),
},
}));
And here I try to use it in my render function as follows
const theme = useTheme();
const classes = useNavStyles(theme);
....
<ListItem button className={classes.navLinkItem}
selected={selectedEntry === title}
....
How can this be resolved?
Thank you.
#Gnut's suggestion is correct, but I thought I'd post an example.
The CSS section in https://material-ui.com/api/list-item-text/#css describes class attributes you can apply to effect style changes in various parts of the controls. Here's an example of how to style the primary text in a ListItemText:
const useStyles = makeStyles({
list: {
width: 300,
border: "1px solid gray"
},
text: {
fontWeight: "bold",
fontFamily: "courier",
color: "blue",
backgroundColor: "orange"
}
});
export default function App() {
const classes = useStyles();
return (
<div className="App">
<List className={classes.list}>
<ListItem button>
<ListItemText primary="Unstyled" />
</ListItem>
<ListItem button>
<ListItemText primary="Styled" classes={{ primary: classes.text }} />
</ListItem>
</List>
</div>
);
}
Codesandbox here: https://codesandbox.io/s/inspiring-euclid-gs7fp
You can have a look at their docs:
How to apply styles to deeper elements
The list of customization points for ListItem is listed here, under CSS section:
ListItem API
At first glance button, root and container should be your first tries.

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>

MUI - Styling text inside ListItemText

I'm trying to apply style to the text inside a ListItemText (MUI):
const text = {
color: 'red'
}
<ListItem button><ListItemText style={text} primary="MyText" /></ListItem>
But the rendered Typograhy element inside is not styled at all ("MyText" is not red).
Looking at the generated code, it seems that the default CSS rules for Typography > subheading is overriding my CSS.
Thanks for your help
Edit: In the first version of the question, there was a misake ("className" instead of "style" prop on ListItemText, sorry about that).
I beleive the only way to achieve this right now is to use the 'disableTypography' prop of the ListItemText element.
<ListItemText
disableTypography
primary={<Typography variant="body2" style={{ color: '#FFFFFF' }}>MyTitle</Typography>}
/>
This lets you embed your own text element with whatever styling you want on it.
this is good one, you can implement without disabling typography
<ListItemText
classes={{ primary: this.props.classes.whiteColor }}
primary="MyTitle"
/>
Per the documentation, the <ListItemText /> component exposes the prop primaryTypographyProps, which we can use to accomplish what you're attempting in your question:
const text = {
color: "red"
};
<ListItem button>
<ListItemText primaryTypographyProps={{ style: text }} primary="MyText" />
</ListItem>
Hope that helps!
<ListItem >
<Avatar style={{ backgroundColor: "#ff6f00" }}>
<LabIcon />
</Avatar>
<ListItemText
primary={<Typography variant="h6" style={{ color: '#ff6f00' }}>Lab</Typography>}
secondary="Experiments" />
</ListItem>
Turns out there's an even better way to do this as such:
const styles = {
selected: {
color: 'green',
background: 'red',
},
}
const DashboardNagivationItems = props => (
<ListItemText
classes={{ text: props.classes.selected }}
primary="Scheduled Calls"
/>
)
export default withStyles(styles)(DashboardNagivationItems)
You can read more about how this is done here: https://material-ui-next.com/customization/overrides/#overriding-with-classes
Method 1
const textColor = {
color: "red"
};
<ListItemText primaryTypographyProps={{ style: textColor }} primary="BlodyLogic" />
For the Secondary Text
const secondaryColor = {
color: 'green'
}
<ListItemText secondaryTypographyProps={{ style: secondaryColor }}
secondary="If you say that you" />
Method 2
<ListItemText
primary={
<Typography variant="caption" display="block" gutterBottom>
caption text
</Typography>
}
/>
Custom design:
const useStyles = makeStyles({
text: {
color: 'red',
fontSize: 49
},
});
/.....// all make classes
<ListItemText
primary={
<Typography className={classes.text}>
caption text
</Typography>
}
/>
Offical Docs
MUI v5 update
You can leverage system properties in Typography to directly add styling props in the primary and secondary components inside the ListItemText:
<ListItemText
primary="Photos"
secondary="Jan 9, 2014"
primaryTypographyProps={{
fontSize: 22,
color: 'primary.main',
}}
secondaryTypographyProps={{
fontSize: 15,
color: 'green',
}}
/>
You can also use styled if you want to reuse ListItemText in multiple places:
import MuiListItemText from '#mui/material/ListItemText';
import { styled } from '#mui/material/styles';
const ListItemText = styled(MuiListItemText)({
'& .MuiListItemText-primary': {
color: 'orange',
},
'& .MuiListItemText-secondary': {
color: 'gray',
},
});
Live Demo
If you are using material-ui 3.x, this is how it is done:
import { withStyles } from '#material-ui/core/styles';
const styles = {
listItemText: {
color: 'white',
},
}
class YourComponent extends Component {
...
render() {
const { classes } = this.props; // this is magically provided by withStyles HOC.
return (
<ListItem button>
<ListItemIcon>
<DashboardIcon />
</ListItemIcon>
<ListItemText classes={{ primary: classes.listItemText }} primary="My Bookings" />
</ListItem>
);
...
}
export default withStyles(styles)(YourComponent);
set all your text related styles on primary property. Sad that it's hidden so deep in the documentation. https://material-ui.com/api/list-item/
Material v1.0
I would add something to #SundaramRavi in regards to:
the way he is using style element which is not great as for Material v1.0 (read the very important difference between v0.16+ and v1.0.
the way files can be structured for better separation of concern.
Whatever.styles.js
const styles = theme => ({
white: {
color: theme.palette.common.white
}
});
exports.styles = styles;
Whatever.js
const React = require('react');
const PropTypes = require('prop-types');
const {styles} = require('./Whatever.styles');
class Whatever extends React.Component {
constructor(props) {
super(props);
}
render() {
const {classes} = this.props;
return (
<div>
<ListItemText
disableTypography
primary={
<Typography type="body2" style={{body2: classes.white}}>
MyTitle
</Typography>
}
/>
</div>
);
}
}
Whatever.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired
};
exports.Whatever = withStyles(styles, {withTheme: true})(Whatever);
you can easily style text by using & span
const useStyles = makeStyles(theme => ({
listItem: {
"& span": {
color: "red"
}
}
}));
..
..
..
<ListItem button>
<ListItemIcon>
<SendIcon />
</ListItemIcon>
<ListItemText className={classes.listItem} primary="Sent mail"/>
</ListItem>
If your are using "#material-ui/core": "^4.11.4" (4.X.X or newer version) then it's simple:
#1st step: Define your styles like this:
const useStyles = makeStyles((theme: Theme) =>
createStyles({
// Other Styling if you wish to use it
root: {
width: '100%',
maxWidth: '36ch',
backgroundColor: theme.palette.background.paper
},
// Other Styling if you wish to use it
inline: {
display: 'inline'
},
// Styling that you asked for
textColor: {
color: 'red'
}
}),
);
#2nd step: Define a constant to use the specific Styles like this:
const AlignItemsList = (props: any) => {
const classes = useStyles(); // Like this one
......
}
#3rd step: In your ListItemText component do like this:
const AlignItemsList = (props: any) => {
const classes = useStyles();
......
<ListItemText
primary="Your Text Goes Here"
classes={{ primary: classes.textColor }} // Like this one
...
/>
};
#4th & final step: Just export your component normally without any other stuff, like this:
export default AlignItemsList;
MUI v5
I recommend to you use global styles for all components. For example you can override styles when you use createTheme.
Here is small example:
export default createTheme({
components: {
MuiListItemText: {
styleOverrides: {
root: {
marginTop: 0,
marginBottom: 0,
},
primary: {
fontSize: '1rem',
},
secondary: {
fontSize: '0.8rem',
},
},
},
},
});
More details on official page

Resources