Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Material-UI ListItem children on hover/active

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;
        }
    }
`;
like image 599
ZenMaster Avatar asked Jun 15 '18 17:06

ZenMaster


3 Answers

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
  }
}
like image 181
Patel Charul Avatar answered Nov 12 '22 04:11

Patel Charul


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
  }
}
like image 32
Akash Kumar Seth Avatar answered Nov 12 '22 06:11

Akash Kumar Seth


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

Codesandbox Demo

like image 24
NearHuscarl Avatar answered Nov 12 '22 06:11

NearHuscarl