is it possible to override a theme override with component classes prop? - material-ui

I am wondering if there is a neat trick to use a component classes prop to override some CSS defined in a theme's overrides.
For instance :
If I want all Button components to have a different font-size than the default one. I can use the theme.overrides props to do so :
// this works, all Buttons text is 1.1rem
let theme = createMuiTheme({
overrides: {
MuiButton: {
label: {
"&": {fontSize: "1.1rem"}
}
}
}
})
Now if for some reason one of my button needs to have a different font-size, I was hoping using classes prop would do the job :
const useClasses = makeStyles({
smallerFontSize: {
fontSize: "0.9rem"
}
})
...
const classes = useClasses()
...
<Button
classes={{
// unfortunately this doesn't work, theme overrides is taking precedence
label: classes.smallerFontSize
}}
>
Some smaller text
</Button>
...
Since using classes prop allows us to target and override some component's CSS if default theme values have not been overridden, I find it confusing that theme overrides end up behaving somewhat differently and have a higher specificity than a one time rule.
I'd argue it kind of defeats the purpose of having a customisable theme.
But hopefully I'm missing something and your wisdom will help !
UPDATE
My mistake was to export the created theme and the makeStyles hook from the same module file.
Doing that made Mui insert theme <style> after the hook <style>.
To fix the issue and be able to use classes component props as I wanted to :
export theme and hooks from separate modules
make sure theme module has no dependency on the module exporting the hook
make sure when using ThemeProvider that it has no parent component importing the hook
I still don't quite understand why things worked before I added the overrides property on the theme object though.

Related

How can I display the material-ui theme object in storybook

I would like to display our theme object in storybook somehow but haven't found a way to even look at theme from inside a component.
Minimally I would like something like what material-ui does showing the theme structure here: https://mui.com/customization/default-theme/
Idealy I would like to map through parts of the object and display colors, font sizes, font weights, spacing etc kind of like this (but it doesn't work because you can't access the theme like this)
const useStyles = makeStyles((theme) => theme.palette.primary);
export const Primary = (): JSX.Element => {
const classes = useStyles;
return (
<>
{Object.keys(classes).map((key) => <Box bgcolor={classes[key]}>{key}</Box>)}
</>
)
};
As the mui.com website is open source, you can implement the minimum by copying the source for the theme explorer on the Default Theme page. You can see how they traverse the theme object, similar in a way to your code snippet.
If you only want to highlight parts of the theme file, you can traverse the object and use some inbuilt components like ColorPalette from #storybook/addon-docs for example. In the MUI project on the palette page, they manually pull out each color using useTheme().
Maybe an storybook addon might be better placed? Example usage with MUI at the bottom of the page: https://storybook.js.org/addons/#react-theming/storybook-addon/

Material UI - Theme is overriding the wrong component

I've made two Material-UI components. Both are using themes. However, the overrides on one theme are being used in the other! I'm losing my mind trying to find a solution to this. Here is the code:
This is being used for component1:
const themeForComponent1 = createMuiTheme({
overrides: {
MuiInputLabel: {
root: {
padding: '13px 0 0 13px !important'
}
},
},
})
...and it is changing stuff in component 2, which happens to also have an element with the class "MuiInputLabel-root".
(edit: If it helps at all, the component is being called in Formik using the Field element via the "as" prop. Each component is using its own ThemeProvider element.)
If you want to have two themes independent you can see the Material UI documentation here
https://material-ui.com/styles/advanced/
But in the fact yo should use two theme providers. I say you that you should use an only theme and customize your component styles using Material UI functions to do that like CSS class

How to extend HTML attribute interfaces when designing reasonml react components?

I'm learning reasonml and quite excited about it. Something I often do in typescript react code is:
type Props = React.HTMLProps<HTMLButtonElement> & { foo: boolean }
const SuperButton: React.FC<Props> = (props) => <button {/* stuff with props */ />
In this regard, I communicate to my users as a component library provider that this button extends normal HTML button attributes.
How can I express and extend normal html component attributes in my components?
I see that reason explicitly doesn't support spreading props: https://github.com/reasonml/reason-react/blob/master/docs/props-spread.md.
I do see that there is a composition strategy: How to compose props across component in reason-react bindings?, but not sure how to marry that up with normal HTML element component stuffs.
Any recommendations? Thanks!
It's possible to do something similar using ReasonReact.cloneElement, as Amirali hinted. The idea is to split up your component's props and the HTML button's props into two separate parameters for your component, render your button, and then clone it while also injecting the extra button props.
This page shows a component which encapsulates this clone-and-injection functionality:
module Spread = {
[#react.component]
let make = (~props, ~children) =>
ReasonReact.cloneElement(children, ~props, [||]);
};
Now, you can use this Spread component for your SuperButton component:
module SuperButton = {
[#react.component]
let make = (~foo, ~htmlButtonProps) =>
<Spread props=htmlButtonProps>
<button> (foo ? "YES" : "NO")->React.string </button>
</Spread>;
};
The htmlButtonProps prop will contain the regular HTML button props, while separately foo is your component's specific prop. The component can be used like this:
<SuperButton foo=true htmlButtonProps={"autofocus": true} />
Small housekeeping note: you don't actually need to define the modules with the module keyword. If you want you can put them in separate files called Spread.re and SuperButton.re. Reason files automatically become modules.

How to modify font-size/style of Tooltip component?

Not able to assign custom class to tooltip component in order to update the style of tooltip component
Tried by assigning to classes property
Posted at https://codesandbox.io/s/twilight-fire-907mp
Make your own Tooltip with custom styles and then use it:
const TooltipWithBiggerFontSize = withStyles({
tooltip: {
fontSize: 30
}
})(Tooltip)
As you already did with other components, eg: DialogContent or DialogActions.
Usage
<TooltipWithBiggerFontSize title="Custom Search">
<Publish />
</TooltipWithBiggerFontSize>
BTW, your code is confusing. You are mixing functional and class components. Once, you are styling with withStyles once with makeStyles.
You should choose one method and stick to it. Here is styling docs. It will solve most of your problems.
codesandbox

How to use CSS API of Grid Component

I'm trying to use the Grid component but I can't find out how to use the CSS API. Ths docs doesn't help me. I just don't get it..
Can someone please help me ?
I know this is not a really good place, sorry, but I can't find any answer anywhere confused
Ideally, you'd set direction to row and override the direction-xs-row class with the name of a class you define (which would set direction to column-reverse), but there are no classes exposed for overriding row for any breakpoint.
You could go the other way, setting direction to column-reverse and overriding direction-*-column-reverse (for all other breakpoints), but that would be tedious and somewhat insane.
The only way to do this at the moment would be to set the className prop to apply some responsive styling via JSS and withStyles:
// create a class that will set flex-direction for the xs breakpoint
const styles = theme => ({
[theme.breakpoints.down('xs')]: {
responsiveDirection: {
flexDirection: 'column-reverse',
},
},
});
// use withStyles to make the class available via the `classes` prop
export default withStyles(styles)(InteractiveGrid);
Then pass your class name, classes.responsiveDirection in this example, as the Grid's className prop:
{/* we would normally destructure classes from this.props */}
<Grid
container
className={this.props.classes.responsiveDirection}
>
Check this codesandbox for a working example.