I'm trying to override the style of the selected TreeItem inside a Material-UI TreeView component. According to the CSS API docs, there is a selected
selector, but when I use that, I see the entire subtree getting styled, and not just the selected item.
What is the correct selector to use to only style the selected tree item?
Code Sandbox: https://codesandbox.io/s/nostalgic-flower-e85cd?file=/src/App.js
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import MuiTreeItem from "@material-ui/lab/TreeItem";
import { withStyles } from "@material-ui/core/styles";
const useStyles = makeStyles({
root: {
height: 240,
flexGrow: 1,
maxWidth: 400
}
});
const TreeItem = withStyles({
selected: {
color: "red"
}
})(MuiTreeItem);
export default function FileSystemNavigator() {
const classes = useStyles();
return (
<TreeView
className={classes.root}
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
<TreeItem nodeId="1" label="Applications">
<TreeItem nodeId="2" label="Calendar" />
<TreeItem nodeId="3" label="Chrome" />
<TreeItem nodeId="4" label="Webstorm" />
</TreeItem>
<TreeItem nodeId="5" label="Documents">
<TreeItem nodeId="10" label="OSS" />
<TreeItem nodeId="6" label="Material-UI">
<TreeItem nodeId="7" label="src">
<TreeItem nodeId="8" label="index.js" />
<TreeItem nodeId="9" label="tree-view.js" />
</TreeItem>
</TreeItem>
</TreeItem>
</TreeView>
);
}
When you have questions about how to override the default Material-UI styles, the best resource is to look at how the default styles are defined.
Below are the default styles for TreeItemContent:
const StyledTreeItemContent = styled(TreeItemContent, {
name: 'MuiTreeItem',
slot: 'Content',
overridesResolver: (props, styles) => {
return [
styles.content,
styles.iconContainer && {
[`& .${treeItemClasses.iconContainer}`]: styles.iconContainer,
},
styles.label && {
[`& .${treeItemClasses.label}`]: styles.label,
},
];
},
})(({ theme }) => ({
padding: '0 8px',
width: '100%',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
WebkitTapHighlightColor: 'transparent',
'&:hover': {
backgroundColor: (theme.vars || theme).palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
},
},
[`&.${treeItemClasses.disabled}`]: {
opacity: (theme.vars || theme).palette.action.disabledOpacity,
backgroundColor: 'transparent',
},
[`&.${treeItemClasses.focused}`]: {
backgroundColor: (theme.vars || theme).palette.action.focus,
},
[`&.${treeItemClasses.selected}`]: {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})`
: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
'&:hover': {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))`
: alpha(
theme.palette.primary.main,
theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity,
),
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})`
: alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
},
},
[`&.${treeItemClasses.focused}`]: {
backgroundColor: theme.vars
? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))`
: alpha(
theme.palette.primary.main,
theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity,
),
},
},
[`& .${treeItemClasses.iconContainer}`]: {
marginRight: 4,
width: 15,
display: 'flex',
flexShrink: 0,
justifyContent: 'center',
'& svg': {
fontSize: 18,
},
},
[`& .${treeItemClasses.label}`]: {
width: '100%',
// fixes overflow - see https://github.com/mui/material-ui/issues/27372
minWidth: 0,
paddingLeft: 4,
position: 'relative',
...theme.typography.body1,
},
}));
The overall structure of TreeItem can be found by looking at TreeItem and TreeItemContent and is as follows (simplified slightly):
<li className="MuiTreeItem-root">
<div className="MuiTreeItem-content">
<div className="MuiTreeItem-iconContainer">
{icon}
</div>
<div className="MuiTreeItem-label">
{label}
</div>
</div>
{children}
</li>
Additionally, the Mui-selected
, Mui-expanded
, Mui-focused
, and Mui-disabled
classes get added to the MuiTreeItem-content
div when applicable.
You can target the MuiTreeItem-content
div as follows:
const TreeItem = styled(MuiTreeItem)`
& > .MuiTreeItem-content.Mui-selected {
color: red;
}
`;
If you don't want to include the icon in the styling, then you can just target the label within the content:
const TreeItem = styled(MuiTreeItem)`
& > .MuiTreeItem-content.Mui-selected .MuiTreeItem-label {
color: red;
}
`;
If you want to change the background color, you need to pay attention to a few more details in the default styling (since the default styling does a lot with background color) in order to deal appropriately with the hover and focused states.
The answers so far use withStyles
from MUI v4 because that was the current version when they were posted. I'm pretty sure there is a MUI v5 approach, but I gave up on trying to get that to work.
The technique used doesn't really matter as much, anyway: The problem is getting all the selectors right.
I ended up adding this to index.css
which seems to disable any most focused
/ hover
/ selected
styles (some edge cases seem to persist):
.MuiTreeItem-root:hover {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content:hover {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content:hover > .MuiTreeItem-label {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected:hover {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected > .MuiTreeItem-label {
background: transparent
}
.MuiTreeItem-root > .MuiTreeItem-content.Mui-selected.Mui-focused {
background: transparent
}
Yes, as far as I can tell, they are all required. Probably still a few more.
NOTE: That's focused
(with one s
), using e.g. MUI-focussed
fails silently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With