How to override nested child component styling in MaterialUI v5? - css

I have a ToggleButton component that has custom styling. I want to make that Toggle Button component look differently only when it's used as a child (inside) a ToggleButtonGroup. Here is how I'd call the ToggleButtonGroup component:
<ToggleButtonGroup onChange={()=>{}} ariaLabel='platform'>
<ToggleButton label='Japan' value='1' selected={true}/>
<ToggleButton label='China' value='2' selected={false}/>
<ToggleButton label='Brazil' value='3' selected={false}/>
</ToggleButtonGroup>
Here is the code for my ToggleButtonGroup component:
const StyledToggleGroup = styled(ToggleOptionsGroup)(
({ theme: { palette, spacing } }) => ({
height: spacing(10),
borderRadius: '6px',
boxShadow: 'none',
'&:hover': {
boxShadow: 'none',
},
'&.MuiToggleButtonGroup-root':{
gap: '0px'
},
'&.MuiToggleButton-standard':{
backgroundColor:'red',
},
'&.MuiToggleButtonGroup-grouped.Mui-selected': {
backgroundColor:'green',
}
})
);
const ToggleButtonGroup:React.FC<ToggleButtonGroupProps> = ({children, onChange, ariaLabel}) =>{
return(
<StyledToggleGroup
exclusive
onChange={onChange}
aria-label={ariaLabel}>
{children}
</StyledToggleGroup>
)
}
However, the last two classes:
'&.MuiToggleButton-standard':{
backgroundColor:'red',
},
'&.MuiToggleButtonGroup-grouped.Mui-selected': {
backgroundColor:'green',
}
don't really change anything.
How can I change the styling of my ToggleButton component only when it's passed as a child to the ToggleButtonGroup component?

It seems that class names are correct according to MUI document, the posted code should just need to specify these as children of StyledToggleGroup with a descendant combinator after & for the styles to work:
Tested in a simple demo here: stackblitz
"& .MuiToggleButton-standard": {
backgroundColor: "hotpink",
},
"& .MuiToggleButtonGroup-grouped.Mui-selected": {
backgroundColor: "lightblue",
},

Related

MUI KeyboardDatePicker change styles of date input box

Currently, I am trying to modify KeyboardDatePicker board color, size, font, padding, but unfortunately, all approaches don’t work. I tried so far:
1 . useStyles :
const useStyles = (params: any) =>
makeStyles(() =>
createStyles({
componentStyle: {
width: params.width ? params.width : 'auto',
color: params.color ? params.color : 'inherit',
verticalAlign: 'middle',
fontSize: '12px',
border: 'solid 2px #0070D8',
},
})
);
Doesn’t override and a border appears on current KeyboardDatePicker border, size doesn’t change as well.
2 . Theme provide, it overrides calendar theme, but not KeyboardDatePicker date box.
<ThemeProvider theme={theme}>
3 . Add styles into KeyboardDatePicker, it is the only working approach
style={{width:"246px",height:"44px"}}
How would you suggest modifying styles of KeyboardDatePicker, and yes style={} approach it's not the correct way to changes styles. p.s I am using Material-UI 4
My KeyboardDatesPicker:
<KeyboardDatePicker
format="MM/dd/yyyy"
margin="normal"
id="date-picker-inline"
defaultValue={props.value}
value={selectedDate}
required={props.required}
showTodayButton={true}
disableToolbar
inputVariant="outlined"
variant="inline"
onChange={(selectedDate) => setSelectedDate(selectedDate)}
KeyboardButtonProps={{
"aria-label": "change date",
}}
keyboardIcon={<Icon icon={ICONS.Cool_icon} />}
className={classes.componentStyle} // do not overide , but puts on top
/>
makeStyles is a hook factory that returns a style hook (usually called useStyles), this is how it's used:
const useStyles = makeStyles(...);
In your code, you define useStyles as a function that return makeStyles instead of telling makeStyles to create a new hook which doesn't make sense here, so change your code to the above. I also fixed the styles for you. The text color styles should be placed in InputBase component:
const useStyles = makeStyles(() =>
createStyles({
componentStyle: {
verticalAlign: "middle",
fontSize: "12px",
width: (params) => (params.width ? params.width : "auto"),
"& fieldset": {
border: "solid 2px #0070D8"
},
"& .MuiInputBase-root": {
height: (params) => (params.height ? params.height : "auto"),
color: (params) => (params.color ? params.color : "inherit")
}
}
})
);
const classes = useStyles({
color: "red",
width: 400,
height: 80,
});
<KeyboardDatePicker
onChange={() => {}}
inputVariant="outlined"
InputProps={{
className: classes.componentStyle
}}
/>
If you want to style via createMuiTheme, here is the equivalent code. Note that you can't pass the component props to create dynamic styles unlike the useStyles approach above:
const theme = createMuiTheme({
overrides: {
MuiTextField: {
root: {
verticalAlign: "middle",
fontSize: "12px",
width: 150,
"& fieldset": {
border: "solid 2px #0070D8"
}
}
}
}
});
And it should work again. For reference, see this section to know how you can use makeStyles with component props.
It seems you don't need to write a custom hook like this useStyles = (params: any) => ..., the hook returned by makeStyles already accepts a props param.
When styling MUI components you need to check the API for each component to define the object you pass to makeStyles, in this case, the date picker component is a group of other MUI components, if you go to the API you'll see different props to pass to each individual component. To style the input you pass the classes returned by the useStyle hook in InputProps, with root rule as it is in the Input API, apply other rules if you need more specific styles.
const useInputStyles = makeStyles({
root: {
width: (props) => (props.width ? props.width : "auto"),
color: (props) => (props.color ? props.color : "inherit"),
verticalAlign: "middle",
fontSize: "12px",
border: "solid 2px #0070D8"
}
});
...
const inputClasses = useInputStyles()
...
<KeyboardDatePicker
...
InputProps={{ classes: inputClasses }}
/>
and to style the "board", not sure if you mean the popover, since you use the inline variant, you pass the styles in the PopoverProps, defining the styles in the paper rule as described in the Popover API
const usePopoverStyles = makeStyles({
paper: {
backgroundColor: "green"
}
});
...
const popoverClasses = usePopoverStyles();
...
<KeyboardDatePicker
...
PopoverProps={{ classes: popoverClasses }}
/>
you can see it working here https://codesandbox.io/s/mui-keyboarddatepicker-styles-sueqd?file=/src/App.tsx
I have implemented it like this in TS, not a finished component, but I hope that this assists people. I am about to add MuiFormLabel-root etc to add more specific styling to the label.
const useDatePickerStyles = makeStyles<ITheme, ITextFieldStyleProps>((theme) =>
createStyles({
datePickerContainer: ({ isValid, isError }) => ({
border: 'solid',
borderRadius: 4,
borderWidth: theme.mvf.border.width.thin,
borderColor: theme.mvf.palette.border,
...(!isError && {
'&:hover': {
boxShadow: theme.mvf.boxShadow.primary,
borderColor: theme.mvf.palette.primary.main,
},
...(isValid && {
color: theme.mvf.palette.primary.main,
boxShadow: theme.mvf.boxShadow.primary,
borderColor: theme.mvf.palette.primary.main,
}),
}),
...(isError && {
color: theme.mvf.palette.error,
boxShadow: theme.mvf.boxShadow.error,
borderColor: theme.mvf.palette.error,
}),
}),
datePicker: () => ({
margin: theme.mvf.spacing.small,
}),
}),
);
export default useDatePickerStyles;
And get access to classes like so
const DatePicker: DatePickerType = ({
id,
onChange,
format,
value,
label,
errorMessage,
placeholder,
isVerticallyCentered,
...props
}: IDatePickerProps) => {
const isValid = !errorMessage && !!value;
const classes = useDatePickerStyles({
isError: !!errorMessage,
isVerticallyCentered,
isValid,
});
return (
<div className={classes.datePickerContainer}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
id={id}
fullWidth
maxDateMessage={''}
minDateMessage={''}
invalidDateMessage={''}
className={classes.datePicker}
label={isVerticallyCentered ? undefined : label} // don't show as label will be outside
placeholder={placeholder}
format={format} // of the displayed date
emptyLabel={placeholder} // displayed value if empty
name="datePicker"
InputLabelProps={{
className: classes.inputLabel,
}}
margin="normal"
value={value}
onChange={onChange}
InputProps={{
className: classes.inputPropsClasses,
inputProps: { className: classes.textInput },
disableUnderline: true,
}}
inputVariant="standard"
KeyboardButtonProps={{
className: classes.calendarButton,
}}
{...props}
/>
</MuiPickersUtilsProvider>
</div>
);
};

Change Material-UI outlined Chip focus and hover color

Trying to add styles to a Material-UI chip (outlined variant) upon hovering, but not getting the expected results.
The border color is white, but the background color doesn't change at all.
So I'm questioning whether backgroundColor is even the right property anymore, but what else can it be?
const CustomChip = withStyles(theme => ({
root: {
"&:hover": {
borderColor: "white",
backgroundColor: "green"
}
}
}))(Chip);
Below are the default background-color styles for the outlined variant of Chip:
/* Styles applied to the root element if `variant="outlined"`. */
outlined: {
backgroundColor: 'transparent',
'$clickable&:hover, $clickable&:focus, $deletable&:focus': {
backgroundColor: fade(theme.palette.text.primary, theme.palette.action.hoverOpacity),
},
In the styles above, $clickable& will be resolved to .MuiChip-clickable.MuiChip-outlined. The important aspect being that this rule is specified using two class names in addition to the pseudo-class (:hover or :focus). This means that these default styles will have greater specificity than the style rule you used for your override (which only uses one class name plus the pseudo-class). In order for your override to be successful, it needs to have specificity equal to or greater than the default styles.
One simple way to do this is to double the &. This causes the generated class name (which the ampersand refers to) to be specified twice in the rule -- increasing its specificity to match the default styles.
Here's a working example:
import React from "react";
import { makeStyles, withStyles } from "#material-ui/core/styles";
import Avatar from "#material-ui/core/Avatar";
import Chip from "#material-ui/core/Chip";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
justifyContent: "center",
flexWrap: "wrap",
"& > *": {
margin: theme.spacing(0.5)
}
}
}));
const StyledChip = withStyles({
root: {
"&&:hover": {
backgroundColor: "purple"
},
"&&:focus": {
backgroundColor: "green"
}
}
})(Chip);
export default function SmallChips() {
const classes = useStyles();
const handleClick = () => {
console.info("You clicked the Chip.");
};
return (
<div className={classes.root}>
<StyledChip variant="outlined" size="small" label="Basic" />
<StyledChip
size="small"
variant="outlined"
avatar={<Avatar>M</Avatar>}
label="Clickable"
onClick={handleClick}
/>
</div>
);
}

Trying to override the css of AppBar from React Material Ui,

<AppBar className={classes.header}/>
The suspect
const classes = useStyles();
const useStyles = makeStyles((theme: Theme) =>
createStyles({
header: {
borderTop: '10px',
borderTopColor: '#367BB5'
},
grow: {
flexGrow: 1
},
menuButton: {
marginRight: theme.spacing(2),
}
);
The grow, and menuButton styling came from the AppBar code directly from Material Ui, but I am trying to implement the header style into the AppBar, unsuccessfully, I have read the documentation but its not very clear to me.
export default Header;
EDIT:
The docs say that the Style sheet name: MuiAppBar but whenever I change header to that, it doesn't change anything
const useStyles = makeStyles({
root: {
position: 'static', // doesnt work
borderTop: '100px', // doesnt work
borderTopColor: '#367BB5', // doesnt work
backgroundColor: '#fafafa',
},
})
<AppBar position="static" classes={{ root: classes.root }}>
Doing this allows me to change the backgroundColor, but the other properties don't work. Not sure why
It's important that you follow this page: https://material-ui.com/customization/theming/
You need to use the ThemeProvider in order to custom the style.

How to change the text color of the selected row in material ui table

I am trying to change the color of the row text and the background color of row on selection.
I am able to change the background color successfully but I am not able to change the text color.
<TableRow
className={classes.tableBody}
>
tableBody: {
"&:focus": {
color: "yellow !important",
backgroundColor: "#3D85D2 !important",
},
},
The background color is controlled in TableRow. In order to get the correct specificity (you shouldn't ever need to leverage "!important" when overriding Material-UI styles), you need to leverage the "hover" class similar to what is done within TableRow.
The color is controlled in TableCell, so that is the level where you need to control it.
For a working solution, in the styles you would have something like:
const styles = theme => ({
tableRow: {
"&$hover:hover": {
backgroundColor: "blue"
}
},
tableCell: {
"$hover:hover &": {
color: "pink"
}
},
hover: {}
});
then in the rendering:
<TableRow
hover
key={row.id}
classes={{ hover: classes.hover }}
className={classes.tableRow}
>
<TableCell
className={classes.tableCell}
component="th"
scope="row"
>
{row.name}
</TableCell>
Here's a working version based on your sandbox:
Here's a similar example, but using "selected" instead of "hover":
https://codesandbox.io/s/llyqqwmr79
This uses the following styles:
const styles = theme => ({
tableRow: {
"&$selected, &$selected:hover": {
backgroundColor: "purple"
}
},
tableCell: {
"$selected &": {
color: "yellow"
}
},
selected: {}
});
and some state:
const [selectedID, setSelectedID] = useState(null);
and changing the TableRow rendering to be:
<TableRow
hover
key={row.id}
onClick={() => {
setSelectedID(row.id);
}}
selected={selectedID === row.id}
classes={{ selected: classes.selected }}
className={classes.tableRow}
>
v4 of Material-UI will include some changes that should make overriding styles considerably easier (and easier to figure out how to do successfully without looking at the source code).
In v4 of Material-UI, we can use the global class names for the selected state ("Mui-selected") and for TableCell ("MuiTableCell-root") and then we only need to apply a single class to TableRow:
const styles = (theme) => ({
tableRow: {
"&.Mui-selected, &.Mui-selected:hover": {
backgroundColor: "purple",
"& > .MuiTableCell-root": {
color: "yellow"
}
}
}
});
This is the only solution that worked for me
const styles = theme => ({
tableRow: {
'&&:hover': {
backgroundColor: '#0CB5F3',
},
},
});
<TableRow
hover
className={classes.tableRow}
>
const useStyles = makeStyles((theme) => ({
selected: {
backgroundColor: "green !important",
"&:hover": {
backgroundColor: "green !important",
},
},
}));
const classes = useStyles();
<TableRow selected classes={{selected: classes.selected,}}/>

Change outline for OutlinedInput with React material-ui

Quick note: this is not a duplicate of How to change outline color of Material UI React input component?
With material-ui (React) I am unable to delete the outline on hover or focus. The reason I am using this input is to request add a little red border when a warning occurs. I can change the focused and hover styles. This is tested in the following image:
Where this CSS is applied when the input is focused:
outlinedInputFocused: {
borderStyle: 'none',
borderColor: 'red',
outlineWidth: 0,
outline: 'none',
backgroundColor: 'green'
},
Component
<OutlinedInput
disableUnderline={true}
notched={true}
id="adornment-weight"
classes={{root: classes.outlinedInput, focused: classes.outlinedInputFocused}}
value={this.state.budgetValue}
onChange={evt => this.updateBudgetValue(evt)}
onKeyPress={evt => this.handleKeyPress(evt)}
endAdornment={<InputAdornment sposition="end">BTC</InputAdornment>}
/>
As you can see the color of the image is green, but there is still an outline. Even though the outlineWidth is 0 and outline is set to none in the CSS. How can I change / disable this outline?
The key to understanding how to override these styles is to look at how they are defined in the Material-UI source code. The question you referenced also shows some of the syntax needed.
Below is an abbreviated version (I left out the styles that are not related to the outline) of the styles from OutlinedInput.js:
export const styles = theme => {
const borderColor =
theme.palette.type === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)';
return {
/* Styles applied to the root element. */
root: {
position: 'relative',
'& $notchedOutline': {
borderColor,
},
'&:hover:not($disabled):not($focused):not($error) $notchedOutline': {
borderColor: theme.palette.text.primary,
// Reset on touch devices, it doesn't add specificity
'#media (hover: none)': {
borderColor,
},
},
'&$focused $notchedOutline': {
borderColor: theme.palette.primary.main,
borderWidth: 2,
},
'&$error $notchedOutline': {
borderColor: theme.palette.error.main,
},
'&$disabled $notchedOutline': {
borderColor: theme.palette.action.disabled,
},
},
/* Styles applied to the root element if the component is focused. */
focused: {},
/* Styles applied to the root element if `disabled={true}`. */
disabled: {},
/* Styles applied to the root element if `error={true}`. */
error: {},
/* Styles applied to the `NotchedOutline` element. */
notchedOutline: {}
};
};
The "outline" of OutlinedInput is controlled via the border on the NotchedOutline component nested within it. In order to impact that nested element, you need to define a "notchedOutline" class (even if empty) that you can then use to target that element for the different states (e.g. focused, hover) of the parent.
Here's an example that fully removes the border:
import React from "react";
import ReactDOM from "react-dom";
import OutlinedInput from "#material-ui/core/OutlinedInput";
import InputAdornment from "#material-ui/core/InputAdornment";
import { withStyles } from "#material-ui/core/styles";
const styles = theme => ({
root: {
"& $notchedOutline": {
borderWidth: 0
},
"&:hover $notchedOutline": {
borderWidth: 0
},
"&$focused $notchedOutline": {
borderWidth: 0
}
},
focused: {},
notchedOutline: {}
});
function App(props) {
const { classes } = props;
return (
<div className="App">
<OutlinedInput
disableUnderline={true}
notched={true}
id="adornment-weight"
classes={classes}
value={1}
endAdornment={<InputAdornment sposition="end">BTC</InputAdornment>}
/>
</div>
);
}
const StyledApp = withStyles(styles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<StyledApp />, rootElement);
You can use inline style like this:
<MyComponent style={{outline: 'none'}} />
2.4.7 Focus Visible: Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible. (Level AA)
https://www.w3.org/TR/2008/REC-WCAG20-20081211/#navigation-mechanisms-focus-visible
https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0#qr-navigation-mechanisms-focus-visible
OutlinedInput is desined in such a way that you can't turn off its outline you have to use TextField with variant 'outlined' as default and 'none' on focus.
You can see the example of Outlined Input Adornments using TextField here

Resources