ReactJS Material UI component css classes, clarification - css

I understand that to style Material UI components I should use their useStyles, like:
const useStyles = makeStyles(themes => ({
root: {
marginTop: '15px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flexGrow: '0'
},
menuButton: {
color: grey[800],
},
}));
but I'm not sure about why. For example, what if I want to use the same css on several components? Wouldn't it be easier to have a separate css file?

You can export them and reuse in other components, that way you end up with only one makeStyles function which you can probably place in some unified file of styles.
export const useStyles = makeStyles(themes => ({
root: {
marginTop: '15px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flexGrow: '0'
},
menuButton: {
color: grey[800],
},
}));
Why would you do this:
Its a theme based component, say it as you have global theme of using blue buttons but then you are overriding it with green but keeping fonts and other stuff inherited from the theme or possibly discarding entirely.
This method is possibly preference based (and in case your repo is following CSS in JS style and you dont want to mix up CSS) but if you want to enforce styled component in your library then you would use this method on the component which is contained within the component.
You can think of this method as taking in themes and then passing in modifier to the theme that you want to override for the component that uses useStyles class.
With that said, you can use it as CSS class as well in your component.

no you can import it in other files where you need it.
suppose you need root style in another View.jsx example file. then import useStyles to that View.jsx file and then you can use all its classes in that View.jsx file.

Related

What is the use of '& .MuiTextField-root' in { makeStyles } of Material UI?

I am making a project in React JS through some resources using Material-UI. I was creating in Form.js file for my app, and there was some styling given to that as shown below ---
import useStyles from './styles';
const classes = useStyles();
<form autoCapitalize='off' noValidate className={`${classes.root} ${classes.form}`} onSubmit={handleSubmit}> ```
In styles.js --
export default makeStyles((theme) => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
},
form: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'center',
}
}));
Now I am not able to understand the use of
'& .MuiTextField-root': {
margin: theme.spacing(1),
}
Can anyone please help me with this like what is the use of '& .MuiTextField-root' ??
When I try to simply use the
margin: theme.spacing(1),
its not giving margin from top and bottom, but its giving very less margin
from left and right. But using the Former one gives equal and more margin
from each side to all the textfields and Buttons in the . Why is it so?? Any help will be appreciated. Thanks!
Attaching the Screenshot ---FORM Image with '& .MuiTextField-root'
FORM Image WithOut '& .MuiTextField-root', just using root: {margin: theme.spacing(1)} only
If you know SCSS then this is the convention to select the child selector to write class for. As you are using it in a form. So the text fields are the child of the form. That's why you have to access/write it like this.
The following code actually mean, any child containing the class name MuiTextField-root will have the margin css rules in it. Doc
'& .MuiTextField-root': {
margin: theme.spacing(1),
}
& .MuiTextField-root would mean any children of current element with that class
You can use the browser dev tools to identify the slot for the component you want to override. It can save you a lot of time. The styles injected into the DOM by MUI rely on class names that follow a simple pattern: [hash]-Mui[Component name]-[name of the slot].
⚠️ These class names can't be used as CSS selectors because they are unstable, however, MUI applies global class names using a consistent convention: Mui[Component name]-[name of the slot].
If you would like to override the styles of the components using classes, you can use the className prop available on each component. For overriding the styles of the different parts inside the component, you can use the global classes available for each slot, as described in the previous section.

onhover modifies styling of a separate classname within MUI makeStyle

On hover, I want to modify the styling of separate classname within the MUI makestyles function. I have seen answers related to this with plain css but not with MUI and i'm unsure as to how to make it work. In my code, I want to make spacer of a paddingTop of 2px on hover of paddingStabilizer. Here is the example that currently isn't working:
const useStyles = makeStyles((theme) => ({
paddingStabilizer: {
'&:hover ~ spacer': {
paddingTop: '2px',
},
},
spacer: {
marginRight: '5px',
},
}
Thank you!

How do you conditionally apply multiple classes to a single Material UI classes prop

I am using the Material UI Drawer and would like to apply a class called "paperStyle" to the classes prop with the rule name "paper" and then conditionally apply an additional class called "specialPaperStyle" if the user is of a particular type. But I haven't been able to apply two classes to the paper rule, let alone apply it conditionally and wondered if anyone knew what to do?
Here are the key parts of my code:
Styles
const useStyles = makeStyles(theme => ({
paperStyle: {
width: drawerWidth,
boxShadow: 'inset 0 0.3rem 1.5rem rgba(0,0,0,0.19)',
color: theme.palette.primary.main,
[theme.breakpoints.up('lg')]: {
minHeight: '100%',
position: 'relative',
},
},
specialPaperStyle:{
backgroundImage: 'linear-gradient(to bottom right, #27133f, transparent)',
},
}));
Material UI drawer
<Drawer
classes={{
paper: classes.paper,
}}
>
Contents of drawer
</Drawer>
The above works in that my paperStyle class is being applied, but how do I conditionally apply the specialPaperStyle class to paper? For example:
Doesn't work!
<Drawer
classes={{
paper: {`${classes.paperStyle isSpecial && $classes.specialPaperStyle}`},
}}
>
Contents of drawer
</Drawer>
Many thanks,
Katie
material-ui comes with a handy package clsx that makes easier to add classes based on specific conditions. You can pass several class arguments, or also objects that will display the class key if the given value is truthy:
import clsx from 'clsx';
<Drawer
classes={{
paper: {clsx(classes.paperStyle, {[classes.specialPaperStyle]: isSpecial})},
}}
>
Contents of drawer
</Drawer>
docs about clsx on material-ui
How about this?
UPDATED
<Drawer
classes={{
paper: {`${classes.paperStyle} ${isSpecial ? classes.specialPaperStyle : ''}`},
}}
>
Contents of drawer
</Drawer>

Override React AgGrid styling settings using createStyles and withStyles

I'm aware that it's possible to override Ag Grid properties by editing the CSS itself, however I'm wondering if it's possible to use the functionalities built into react to do this instead. I'm relatively new to the two frameworks, so apologies if there's something I'm not understanding.
Ultimately, what I want to do is something like this:
styles.js
---------
const styles = (theme: Theme) =>
createStyles({
root: {
position: 'relative',
height: 'calc(100vh - 128px)',
},
agHeaderCellLabel: {
agHeaderCellText: {
writingMode: 'vertical-lr',
marginTop: '100px',
},
},
})
export default styles
GridComponent.tsx
-----------------
import styles from './styles'
...
return (
<Paper className={classes.root}>
<div
id="myGrid"
style={{
height: '100%',
width: '100%',
}}
className={`ag-theme-material ${classes.agHeaderCellLabel}`}
>
<AgGridReact
// listening for events
onGridReady={onGridReady}
onRowSelected={onRowSelected}
onCellClicked={onCellClicked}
onModelUpdated={calculateRowCount}
// Data
columnDefs={cDef}
defaultColDef={defaultColumnFormat}
suppressRowClickSelection={true}
groupSelectsChildren={true}
debug={true}
rowSelection="multiple"
// rowGroupPanelShow={this.state.rowGroupPanelShow}
enableRangeSelection={true}
pagination={true}
rowData={rows}
/>
</div>
</Paper>
)
...
export withStyles(styles)(GridComponent)
In this example I'm just trying to get the header text to be displayed vertically.
I've inherited this project, and I've noticed that all of the styling has been done in this method, as there are no custom css files lying around, so I'm trying to stick with that convention of a styles file alongside the component.
Is this possible, and if so,
I ran into this same situation, and came up with the following solution. Although not necessarily ideal, it allows you to continue with the desired convention.
styles.js
---------
const styles = (theme: Theme) =>
createStyles({
root: {
position: 'relative',
height: 'calc(100vh - 128px)',
},
//Apply changes to agGrid material HeaderRoot
myClassAppliedToGrid: {
'& .ag-header[ref="headerRoot"]':{
writingMode: 'vertical-lr',
marginTop: '100px',
}
}
//OR
//Apply Changes to agGrid material header row
myClassAppliedToGrid: {
'& .ag-header-row':{
writingMode: 'vertical-lr',
marginTop: '100px',
}
}
})
export default styles
The key idea is using the & SASS syntax to "reach into" agGrid and make more specific CSS classes so you can override them. (see https://css-tricks.com/the-sass-ampersand/ for more info)
The key pieces of info are:
.parent {
& .child {}
}
turns into
.parent .child {}
and
.some-class {
&.another-class {}
}
turns into
.some-class.another-class { }
Using this sytanx, you should be able to create CSS classes that you can apply to your grid, columns, rows, etc that will properly override the material ag-grid theme.
Here is another example, but this class gets applied to a cell using agGrid cellStyleRules when a row is dragged over it, rather than applying the class to the grid as a whole. This way it only effects cells that have a row drag occuring over them:
rowDraggedOverCellsTopEdge: {
'&.ag-cell': {
borderTopColor: theme.palette.gray[50],
borderTopWidth: 8,
backgroundColor: fade(theme.palette.gray[50], 0.3)
}
},
Finally, one thing I did not do but would reccommend investigating is looking into agGrid's theme overriding, especially if you are on version 23+
https://www.ag-grid.com/javascript-grid-themes-provided/#customising-themes
It might be a good idea to get your base overrides to the theme done this way if you expect a consistent look and feel of your grids throughout the application.
Cheers!

Override Material UI Tab Indicator Emotion Styled

Trying to figure out how to override the styles of the tabs indicator using styled from Emotion. I am not sure how to access nested classes. This is what I have, but it isn't getting me there:
const StyledTabs = styled(Tabs)(
{
classes: {
indicator: {
background: 'black',
},
},
}
);
Any help would be awesome!
There are a couple issues. styled from Emotion only supports generating a single class name per usage. It doesn't provide any support for the classes: {indicator: {styles}} structure in your example.
Below is a syntax that allows you to use styled to provide a class name for the "indicator" class of Tabs:
const StyledTabs = styled(({ className, ...other }) => {
return <Tabs {...other} classes={{ indicator: className }} />;
})({
backgroundColor: "black"
});
However, this does not work completely robustly because the <style> element for the Emotion styles does not consistently occur after the <style> elements from JSS (used for Material-UI's styling) in the <head> of the document. I'm not sure how to alter the insertion point for Emotion's styles, but you can read here about how to change the insertion point for JSS. I've included this approach in my sandbox below.
Here's a sandbox that shows this working:
Another syntax option is the following which will allow you to control more than one Tabs class:
const StyledTabs = styled(({ className, ...other }) => {
return <Tabs {...other} classes={{ root: className, flexContainer: "flexContainer", indicator: "indicator" }} />;
})({
"& .indicator": {
background: "black"
},
"& .flexContainer": {
flexDirection: "row-reverse"
}
});

Resources