When I'm trying override background color of root node for ListItem( the attribute selected = {true}), MUI is overriding it by default class from theme (theme.palette.action.selected).
I'd like to know is it normal behaviour or is it a bug (I has fixed it in my local repo and can make pull request)?
Thx
I'd like that background color of ListItem was 'red'.
The background color of ListItem is 'rgba(0, 0, 0, 0.14)'.
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import { withStyles } from '@material-ui/core/styles'
const styles = theme => ({
root: {
},
selected: {
backgroundColor: 'red',
},
})
function Demo(props) {
const { classes } = props
return (
<List component="nav">
<ListItem selected={true}
classes={{
selected: classes.selected
}}>
TEST
</ListItem>
</List>
)
}
export default withStyles(styles)(Demo)
| Tech | Version |
|--------------|---------|
| Material-UI | v3.5.1 |
| React | 16.6.3 |
| Browser | Chrome, Safari |
| TypeScript | No |
| etc. | |
@Rombs The correct solution is the following:
import React from "react";
import { List, ListItem, withStyles } from "@material-ui/core";
const StyledListItem = withStyles({
root: {
backgroundColor: "blue",
"&.Mui-selected": {
backgroundColor: "red"
}
},
})(ListItem);
function Demo() {
return (
<List component="nav">
<StyledListItem>default</StyledListItem>
<StyledListItem selected>selected pseudo-class</StyledListItem>
</List>
);
}
export default Demo;
https://codesandbox.io/s/pk5w045pp7?file=/src/demo.js
You can learn more in https://material-ui.com/customization/components/#pseudo-classes.
I think you can add !important tag to solve this problem.
const styles = theme => ({
root: {
},
selected: {
backgroundColor: 'red !important',
},
})
With v4.0.0-beta.1 you can do:
import React from "react";
import { List, ListItem, styled } from "@material-ui/core";
const StyledListItem = styled(ListItem)({
backgroundColor: "blue",
"&.Mui-selected": {
backgroundColor: "red"
}
});
function Demo() {
return (
<List component="nav">
<StyledListItem>default</StyledListItem>
<StyledListItem selected>selected pseudo-class</StyledListItem>
</List>
);
}
export default Demo;
https://codesandbox.io/s/xwor26oqo
cc @iossocket @ShogunRoss @iwknow @chpinan1128 @ndibek @jay-almaraz @HorizonShadow @TidyIQ
I would love to learn more what would be a great solution for you guys.
This would be fine for me if it worked for makeStyles as well.
I'm trying to create a custom OutlineInput component that changes the border color and icon start adornment color to green if a valid input is entered (right now it's simply considered valid if the value is not empty), as well as change the label and notch to accommodate for a start adornment that doesn't result in the input label starting shrunk.
I can get it to work by forcing overrides using !important
but I would prefer if I could instead use it how you described above, like so:
// ::::::::::::::::::::::::::::::::::::::::::::::::
// CSS
// ::::::::::::::::::::::::::::::::::::::::::::::::
const useStyles = makeStyles(
(theme: Theme): Record<string, CSSProperties> => ({
inputProps: {
marginLeft: theme.spacing(1)
},
inputLabel: {
marginLeft: theme.spacing(5.5)
},
validColor: {
color: theme.custom.palette.success
},
errorColor: {
color: theme.palette.error.main
},
notch: {
paddingLeft: theme.spacing(6.5)
},
notchedValidBorder: {
"& $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&:hover $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&$focused $notchedOutline": {
borderColor: theme.custom.palette.success
}
}
})
);
// ::::::::::::::::::::::::::::::::::::::::::::::::
// Layout
// ::::::::::::::::::::::::::::::::::::::::::::::::
interface LayoutProps {
readonly id: string;
readonly label: InputLabelProps["children"];
readonly labelWidth: OutlinedInputProps["labelWidth"];
readonly labelRef: InputLabelProps["ref"];
readonly type?: OutlinedInputProps["type"];
readonly startAdornmentIcon?: ComponentType<SvgIconProps>;
readonly isEmpty: boolean;
readonly isInvalid: boolean;
readonly toggleEmptyState: ToggleEmptyState;
readonly errorHelperText?: FormHelperTextProps["children"];
}
const Layout: FunctionComponent<LayoutProps> = ({
id,
label,
labelRef,
labelWidth,
type,
startAdornmentIcon,
isEmpty,
toggleEmptyState,
isInvalid,
errorHelperText
}) => {
const classes = useStyles();
const labelClasses: {} | { root: string; focused: string } = isEmpty
? {}
: { root: classes.validColor, focused: classes.validColor };
return (
<FormControl
fullWidth
margin="dense"
variant="outlined"
error={isInvalid && isEmpty}
>
<InputLabel
ref={labelRef}
className={classes.inputLabel}
htmlFor={id}
classes={labelClasses}
shrink={!isEmpty}
>
{label}
</InputLabel>
<OutlinedInput
id={id}
classes={{
root: !isEmpty ? classes.notchedValidBorder : undefined,
notchedOutline: classes.notch
}}
labelWidth={labelWidth}
notched={!isEmpty}
type={type}
inputProps={{
className: classes.inputProps
}}
startAdornment={
startAdornmentIcon ? (
<IconAdornment
icon={startAdornmentIcon}
props={
isInvalid && isEmpty
? { classes: { root: classes.errorColor } }
: !isEmpty
? { classes: { root: classes.validColor } }
: undefined
}
/>
) : (
undefined
)
}
endAdornment={
type === "password" ? <ShowPasswordAdornment /> : undefined
}
onChange={(event): void => toggleEmptyState(event, id)}
/>
{errorHelperText && (
<Collapse in={isInvalid && isEmpty}>
<FormHelperText error>{errorHelperText}</FormHelperText>
</Collapse>
)}
</FormControl>
);
};
This doesn't work. A few of the class changes are not applied and results in it looking like this: https://i.imgur.com/cPVUNG8.png
And it has the following console error:
Warning: [JSS] Could not find the referenced rule notchedOutline in makeStyles.
However, if I use the following code then it works fine:
// ::::::::::::::::::::::::::::::::::::::::::::::::
// CSS
// ::::::::::::::::::::::::::::::::::::::::::::::::
const useStyles = makeStyles(
(theme: Theme): Record<string, CSSProperties> => ({
inputProps: {
marginLeft: theme.spacing(1)
},
inputLabel: {
marginLeft: theme.spacing(5.5)
},
validColor: {
color: `${theme.custom.palette.success} !important`
},
errorColor: {
color: theme.palette.error.main
},
notch: {
paddingLeft: `${theme.spacing(6.5)}px !important`
},
notchedValidBorder: {
borderColor: `${theme.custom.palette.success} !important`,
"&:hover": {
borderColor: `${theme.custom.palette.success} !important`
},
"&:focused": {
borderColor: `${theme.custom.palette.success} !important`
}
}
})
);
// ::::::::::::::::::::::::::::::::::::::::::::::::
// Layout
// ::::::::::::::::::::::::::::::::::::::::::::::::
interface LayoutProps {
readonly id: string;
readonly label: InputLabelProps["children"];
readonly labelWidth: OutlinedInputProps["labelWidth"];
readonly labelRef: InputLabelProps["ref"];
readonly type?: OutlinedInputProps["type"];
readonly startAdornmentIcon?: ComponentType<SvgIconProps>;
readonly isEmpty: boolean;
readonly isInvalid: boolean;
readonly toggleEmptyState: ToggleEmptyState;
readonly errorHelperText?: FormHelperTextProps["children"];
}
const Layout: FunctionComponent<LayoutProps> = ({
id,
label,
labelRef,
labelWidth,
type,
startAdornmentIcon,
isEmpty,
toggleEmptyState,
isInvalid,
errorHelperText
}) => {
const classes = useStyles();
const labelClasses: {} | { root: string; focused: string } = isEmpty
? {}
: { root: classes.validColor, focused: classes.validColor };
return (
<FormControl
fullWidth
margin="dense"
variant="outlined"
error={isInvalid && isEmpty}
>
<InputLabel
ref={labelRef}
className={classes.inputLabel}
htmlFor={id}
classes={labelClasses}
shrink={!isEmpty}
>
{label}
</InputLabel>
<OutlinedInput
id={id}
classes={{
notchedOutline: clsx(
classes.notch,
!isEmpty ? classes.notchedValidBorder : undefined
)
}}
labelWidth={labelWidth}
notched={!isEmpty}
type={type}
inputProps={{
className: classes.inputProps
}}
startAdornment={
startAdornmentIcon ? (
<IconAdornment
icon={startAdornmentIcon}
props={
isInvalid && isEmpty
? { classes: { root: classes.errorColor } }
: !isEmpty
? { classes: { root: classes.validColor } }
: undefined
}
/>
) : (
undefined
)
}
endAdornment={
type === "password" ? <ShowPasswordAdornment /> : undefined
}
onChange={(event): void => toggleEmptyState(event, id)}
/>
{errorHelperText && (
<Collapse in={isInvalid && isEmpty}>
<FormHelperText error>{errorHelperText}</FormHelperText>
</Collapse>
)}
</FormControl>
);
};
And here's the result: https://i.imgur.com/x4YoIaV.png
@ryancogswell created a sandbox that shows that there is no issue having this work using withStyles
here - https://codesandbox.io/s/vx057jo47 - so it appears that there is no solution to overcome this problem for makeStyles
like there is for withStyles
, apart from using !important
to brute force the change.
The sandbox mentioned is from here: https://github.com/mui-org/material-ui/issues/15524#issuecomment-487816785
@TidyIQ Here's a version of that same sandbox using makeStyles
that works just fine: https://codesandbox.io/s/qkwln7q6yq
In your example you have:
notchedValidBorder: {
"& $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&:hover $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&$focused $notchedOutline": {
borderColor: theme.custom.palette.success
}
}
but you aren't defining $notchedOutline
which is what the error is telling you.
You instead need:
notchedValidBorder: {
"& $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&:hover $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&$focused $notchedOutline": {
borderColor: theme.custom.palette.success
}
},
notchedOutline: {} // This line causes $notchedOutline to be defined
then use this something like:
classes={{
notchedOutline: classes.notchedOutline,
root: !isEmpty ? classes.notchedValidBorder : undefined
}}
As I indicated in my comment in #15524, StackOverflow is the more appropriate venue for this type of question.
Thanks for the help, although this seems to be a very confusing way to do it. For example, I changed the CSS to:
notchedOutline: {
paddingLeft: theme.spacing(6.5)
},
notchedValidBorder: {
"& $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&:hover $notchedOutline": {
borderColor: theme.custom.palette.success
},
"&$focused $notchedOutline": {
borderColor: theme.custom.palette.success
}
}
And 1) The paddingLeft
on the notchedOutline
class and color
on the validColor
class still requires the !important
flag to make them work, and 2) I now get an error saying Could not find the referenced rule focused in makeStyles
, which means I also need to add an empty CSS class and it to the list of classes under focused: classes.focused
.
I'm not sure of a better way to do this but perhaps adding more detail about these instructions in the documentation would be a good idea.
@TidyIQ Here's a version of that same sandbox using
makeStyles
that works just fine: https://codesandbox.io/s/qkwln7q6yq
@ryancogswell, But if you pass some props to makeStyles, its not working: https://codesandbox.io/s/outlinedinput-emm9t
But if you pass some props to makeStyles, its not working: https://codesandbox.io/s/outlinedinput-emm9t
@ndeviant This seems like a bug in JSS, but you can work around it by making the empty rules (focused
and notchedOutline
) functions just as the root
rule is a function. It doesn't seem to recognize the existence of non-function rules when it is processing a function rule.
The following works:
const useStyles = makeStyles(theme => ({
root: ({ borderColor }) => ({
boxShadow: `1px 1px 4px 2px ${borderColor}`,
"& $notchedOutline": {
borderColor: borderColor
},
"&:hover $notchedOutline": {
borderColor: borderColor
},
"&$focused $notchedOutline": {
borderColor: borderColor
}
}),
focused: () => ({}),
notchedOutline: () => ({})
}));
I've logged this as an issue: https://github.com/cssinjs/jss/issues/1213
@ryancogswell This is an issue with Material-UI, not JSS, see #15511. I'm not sure that we should
invest resources in JSS, given: #6115.
@oliviertassinari The solution you presented here works, but I think this terrible. Why do you accept a class for selected
item if it is not applied? Why you even put that in the documentation of ListItem
if it does not work? Using class nested from root is only way to do it, but looks like hack to me.
@tommybernaciak See https://material-ui.com/customization/components/#pseudo-classes for the why.
Most helpful comment
With v4.0.0-beta.1 you can do:
https://codesandbox.io/s/xwor26oqo
cc @iossocket @ShogunRoss @iwknow @chpinan1128 @ndibek @jay-almaraz @HorizonShadow @TidyIQ
I would love to learn more what would be a great solution for you guys.