material ui withStyles and WithTheme - How to access theme - reactjs

const MySlider = withStyles({
root: {
color: theme => theme.palette.primary.main
}
}, {withTheme: true})(Slider);
I did expect the withTheme option would make the theme accessible inside withStyles. If so then how? because theme is not defined and its also not found in props ?
Any idea

withStyles function can accept a callback instead of an object giving you the access to the theme object like this
const NewButton = withStyles((theme) => {
root: {
color: theme.palette.primary
}
}, { withTheme: true })(Button);

Related

How to select nested colors from theme in Material UI?

I'm creating a style to use within a Button, but I'm unsure on how to select a nested style from my theme. Here is my style and theme:
const buttonStyle: SxProps<Theme> = {
'&:hover': {
backgroundColor: 'backgroundAlert', // workaround
},
};
export const darkTheme = createTheme(themeOptions, {
palette: {
mode: 'dark',
background: {
default: '#0B0E16',
paper: '#1D1F2B',
alert: '#373B38' // I want to use this property
},
backgroundAlert: '#373B38', // workaround
},
});
I'm using backgroundAlert as a work around because i don't know how to select alert from background.alert since it's nested. I couldnt find anything in the docs - does anyone know the syntax to do this?
Something like the below worked for me.
import { styled } from '#mui/material/styles';
const ButtonStyle = styled(Button)(({ theme }) => ({
'&:hover': {
backgroundColor: darkTheme.palette.background.alert,
},
}));
<ButtonStyle variant="contained">Your Button</ButtonStyle>

makeStyles with TypeScript - pass props to make styles explanation

I'm using Material-UI makeStyles function with TypeScript
I found the solution here on how to do that and it is working:
export interface StyleProps {
md?: any;
}
const useStyles = makeStyles<Theme, StyleProps>({
button: {
width: ({ md }) => md && '50%',
}
});
export default useStyles;
But I'm trying to understand how things work.
I don't understand the <Theme, StyleProps> part. I looked at all solutions on StackOverflow regarding this, no one explained it.
Could someone please clarify it?
Thanks.
Here is my simple explaination about the <Theme, StyleProps>
makeStyles method from MaterialUI is using generic type for defining what u can use from their Theme variables and, props type that u want to assign.
Theme type imported from : import { Theme } from "#material-ui/core";
makeStyles() returning theme value that u can use, the 'Theme' is the type definition for that theme value.
Code Example :
#1 makeStyles using only the theme value :
const useStyles = makeStyles<Theme>((theme) => ({
button: {
background: theme.palette.background.paper,
color: theme.palette.success.light
}
}));
#2 makeStyles using theme value & your props :
const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
button: {
background: theme.palette.background.paper,
color: theme.palette.success.light,
width: ({ md }) => "50%"
}
}));
#3 makeStyles using only your props :
const useStyles = makeStyles<any /* <-- it doesn't matter what type u define here, because u doesn't use theme value*/, StyleProps>({
button: {
width: ({ md }) => "50%",
background: "grey"
}
});
Hope this answer can help. :)

Material UI theme `overrides` not working while using `withStyles`

I am using withStyles HOC to provide classes to a component:
const styles = createStyles({
Appbar: ({ backgroundColor }: BaseHeaderProps) => ({
backgroundColor
}),
// ...moreStyles
});
export default withStyles(styles, { name: 'Component' })(Component);
I also need to be able to override the very same classes/styles in the global theme overrides object:
overrides: {
Component: {
Appbar: {
backgroundColor: 'black',
},
},
// more overrides...
The problem I am facing is that the styles in the overrides object will not apply/override the classes defined in the createStyles. The only way overrides would actually override is if the styles are "prop-independent", something like:
const styles = createStyles({
Appbar: {
backgroundColor: 'some-fixed-color',
),
// ...moreStyles
});
Now, the overrides object will override this class and a black background color will be applied to the Appbar.
What I want is to be able to have this overridable functionality even when using "prop-dependent" styles, as the first one shows.
Maybe this helps: The "prop-dependent" callback that returns the style object NEVER gets called when overrides are used in the Theme. I have tried to log the theme object but it never is called.
In the documentation, another component name for overload: "The MuiAppBar name can be used for providing default props or style overrides at the theme level"
https://v4.mui.com/api/app-bar/#appbar-api
export const CustomTheme = {
overrides: {
MuiAppBar: {
backgroundColor: 'black', // ???
},
// more overrides..
},
};

Material UI: how do I use Theme values with withStyles?

I am building a component with Material UI. I am using the default theme file (as per here https://material-ui.com/customization/default-theme/).
I know I can bring in certain values from the theme with makeStyles as such:
import { makeStyles, Theme } from '#material-ui/styles';
const useStyles = makeStyles((theme: Theme) => ({
something: {
color: theme.palette.common.black,
},
}));
That works fine.
But how do I use those same values with styled Material UI components? eg:
import withStyles from '#material-ui/core/styles';
const StyledBadge = withStyles({
badge: {
color: theme.palette.common.black,
},
})(Badge);
I tried replicating the above, eg:
const StyledBadge = withStyles((theme: Theme) => ({
but this doesn't work.
Would anyone know the correct way of doing this?
I tried creating Component withStyles as below and it is working perfectly.
import { withStyles } from "#material-ui/core";
export const ExpansionPanelDetails = withStyles(theme => ({
root: {
padding: theme.spacing(1),
},
}))(MuiExpansionPanelDetails);

CSS empty definition necessary to have the type as specificity selector (material UI in React)

The styling for the unselected toggle button works nicely.
But the style of the selected toggle button does not appear when you don't define an empty class selector:
./App.js
import * as React from "react";
import { render } from "react-dom";
import { createStyles, WithStyles } from "#material-ui/core";
import { ToggleButtonGroup, ToggleButton } from "#material-ui/lab";
import { withStyles } from "#material-ui/core/styles";
const styles = () =>
createStyles({
root: {
backgroundColor: "blue",
"&$selected": {
color: "blue",
backgroundColor: "yellow"
}
},
// this empty definition needs to be here otherwise it won't work
selected: {}
});
export interface IAppProps extends WithStyles<typeof styles> {}
const App: React.FC<IAppProps> = ({ classes }: IAppProps) => {
return (
<ToggleButton
classes={{
root: classes.root,
selected: classes.selected
}}
selected={true}
>
option 1
</ToggleButton>
);
};
const AppWithStyles = withStyles(styles)(App);
const rootElement = document.getElementById("root");
render(<AppWithStyles />, rootElement);
This perhaps has something to do with the type definition in the props. When you remove the selector 'selected' from the styles definition, it is not available anymore in the interface IAppProps.
How can you define this type in the interface?
Working example: https://codesandbox.io/s/elated-sammet-y7wh7?fontsize=14
update 1: Also tried Augmenting the props as described in the material-ui docs:
const styles = () =>
createStyles({
toggleButton: {
backgroundColor: "blue",
"&$toggleButtonSelected": {
color: "blue",
backgroundColor: "yellow"
}
},
});
export interface IAppProps {
classes: {
toggleButton: string;
toggleButtonSelected: string;
};
}
const App: React.FC<IAppProps> = ({ classes }: IAppProps) => {
// ...
With no luck.
update 2: using a hook makes the type casting redundant, but it also won't fix this:
import * as React from "react";
import { render } from "react-dom";
import { createStyles, makeStyles } from "#material-ui/core";
import { ToggleButtonGroup, ToggleButton } from "#material-ui/lab";
const useStyles = makeStyles(() =>
createStyles({
root: {
backgroundColor: "blue",
"&$selected": {
color: "blue",
backgroundColor: "red"
}
},
// this still needs to be there...
// selected: {}
})
)
export interface IAppProps {}
const App: React.FC<IAppProps> = () => {
const classes = useStyles();
return (
// ...
)
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
I believe you just have a misunderstanding of how JSS works and the meaning of some of the syntax. The relevant documentation is here.
When you define your styles object (or function taking in the theme and returning an object), each key in that object is referred to by JSS as a "rule". The key is the rule name and JSS will translate the value into a CSS class. The classes object that you get back from useStyles or that gets injected as a prop when using withStyles then maps the rule names to the generated CSS class names.
The $ruleName syntax is a way to refer to the CSS class name of one of the other rules in your styles object. The & refers to the parent rule. In your example you have rules called root and selected (when it isn't commented out).
The following:
root: {
backgroundColor: "blue",
"&$selected": {
color: "blue",
backgroundColor: "red"
}
},
selected: {}
would compile to CSS like the following:
.root-0 {
background-color: blue;
}
.root-0.selected-0 {
color: blue;
background-color: red;
}
By passing the following to Material-UI:
classes={{
root: classes.root,
selected: classes.selected
}}
selected={true}
You are telling it to apply "root-0 selected-0" as class names in addition to the class names applied for the default styling. Without the empty selected: {} rule name, you can't refer to $selected from the root rule (JSS should be giving you a warning in the console if you do).
There is a slightly simpler alternative (as of v4) for referring to the selected class name. selected is one of the Material-UI special states that it refers to as pseudo-classes and the documentation provides the default class name for each (e.g. Mui-selected).
This means you can do the following:
root: {
backgroundColor: "blue",
"&.Mui-selected": {
color: "blue",
backgroundColor: "red"
}
}
This is no longer referencing another rule, so selected: {} isn't needed and neither is selected: classes.selected needed in the classes prop. Instead this is referencing the actual class name that Material-UI applies for the default styling when selected={true}.

Resources