MUI v5 passing props to CSS theme using styled() - reactjs

Previously, in Material-UI v4, I had this bit of code
const { customPadding } = props;
const classes = useStyles({
padding: customPadding,
} as any);
To pass props to the classes of an element.
But v5 uses emotion instead of JSS, where I do something like this instead.
const StyledContainer = styled(Container)(({theme}: any) => ({
[`&.${classes.FullPageLayoutRoot}`]: (props: any) => ({
minHeight: `calc(100vh - ${appBarHeight}px - ${theme.spacing(1)} - 1px)`,
display: 'flex',
}),
[`&.${classes.middle}`]: {
alignItems: 'center',
},
[`& .${classes.paper}`]: (props: any) => ({
backgroundColor: grey[800],
marginBottom: theme.spacing(1),
padding: theme.spacing(props.padding),
minWidth: '100%',
})
}));
...
return(
<StyledContainer maxWidth={maxWidth} fixed className={clsx(classes.FullPageLayoutRoot, {
[classes.middle]: middle,
})}>
<Paper className={clsx(classes.paper, classes.padding, className)} {...PaperProps} >
{content}
</Paper>
</StyledContainer>
)
How would I accomplish this in Material-UI v5?

They're passed along with the theme property in the callback:
const MyDiv = styled("div", {
// indicate that this prop name should be used to conditionally
// style the component only and should not be spread to the DOM element.
shouldForwardProp: (propName) => propName !== "isRed"
})(({ theme, isRed }) => ({
backgroundColor: isRed ? "red" : theme.palette.primary.main
}));
export default function ThemeUsage() {
return (
<MyDiv isRed>Styled div with theme</MyDiv>
);
}
Live Demo

Related

How can I pass both the theme and the props in the styled() of Material UI-5?

import {
AppBar,
Avatar,
Badge,
InputBase,
Toolbar,
Typography,
} from "#mui/material";
import React, { useState } from "react";
import { styled, alpha } from "#mui/material/styles";
import { Mail, Notifications, Search } from "#mui/icons-material";
const LogoLg = styled(Typography)(({ theme }) => ({
display: "none",
[theme.breakpoints.up("sm")]: {
display: "block",
},
}));
const LogoSm = styled(Typography)(({ theme }) => ({
display: "none",
[theme.breakpoints.down("sm")]: {
display: "block",
},
}));
const SearchDiv = styled("div")(({ theme, props }) => ({
display: "flex",
alignItems: "center",
backgroundColor: alpha(theme.palette.common.white, 0.15),
borderRadius: theme.shape.borderRadius,
width: "50%",
"&:hover": {
backgroundColor: alpha(theme.palette.common.white, 0.15),
},
[theme.breakpoints.down("sm")]: {
display: props.open ? "flex" : "none",
},
}));
const IconsDiv = styled("div")((theme) => ({
display: "flex",
alignItems: "center",
}));
const BadgeItem = styled(Badge)(({ theme }) => ({
marginRight: theme.spacing(2),
}));
const SearchButton = styled(Search)(({ theme }) => ({
marginRight: theme.spacing(2),
}));
const Navbar = () => {
const [open, setOpen] = useState(false);
return (
<AppBar>
<Toolbar sx={{ display: "flex", justifyContent: "space-between" }}>
<LogoLg variant="h6">Milan Poudel</LogoLg>
<LogoSm variant="h6">MILAN</LogoSm>
<SearchDiv open={open}>
<Search />
<InputBase
placeholder="Search..."
sx={{ color: "white", marginLeft: "10px" }}
/>
</SearchDiv>
<IconsDiv>
<SearchButton onClick={() => setOpen(true)} />
<BadgeItem badgeContent={4} color="error">
<Mail />
</BadgeItem>
<BadgeItem badgeContent={2} color="error">
<Notifications />
</BadgeItem>
<Avatar
alt="milan-poudel"
src="https://i.ytimg.com/vi/CmSc_EIqyQI/maxresdefault.jpg"
/>
</IconsDiv>
</Toolbar>
</AppBar>
);
};
export default Navbar;
In the searchDiv, I want to use both theme and the props that I have used in SearchDiv below (i.e. "open" prop). I want to use it in the styled and according to it's state, want to customize the display property? How can I pass both the theme and props to the styled in the new MUI5? Previously I could use props directly while in the MUIv4 but I don't think in MUI5 it is allowed
Signature from the styled API Documentation:
styled(Component, [options])(styles) => Component
The props are passed into the styles parameter (from where you're also destructuring and retrieving theme), so you can add your open property to that and use it directly -- for example:
// 1. Added `open` to `styles` param
const SearchDiv = styled("div")(({ theme, open }) => ({
...
// 2. Changed `props.open` to `open` below
[theme.breakpoints.down("sm")]: {
display: open ? "flex" : "none",
},
}));
// Unchanged
<SearchDiv open={open}>
...
</SearchDiv>
Simple working example to demonstrate usage: https://codesandbox.io/s/simple-mui5-props-example-1uclg?file=/src/Demo.js

How do customise a component's CSS selectors in FluentUI

I am trying to customise the behavior of an FluentUI component when it is hovered over (A Primary Button in this case). How do I customise CSS selectors when I am using Microsoft's React FluentUI library.
I tried this initially (This approach is deprecated now, in favor of the method where you add selectors as siblings)...
export const MyButton = (props: IButtonProps) => {
const styles: IButtonStyles = {
root: {
backgroundColor: '#000000',
selectors : {
':hover': {
backgroundColor: '#0000ff'
}
}
},
}
return (
<PrimaryButton styles={styles} {...props} />
);
}
Then I tried this:
export const MyButton = (props: IButtonProps) => {
const styles: IButtonStyles = {
root: {
backgroundColor: '#000000',
':hover': {
backgroundColor: '#0000ff'
}
},
}
return (
<PrimaryButton styles={styles} {...props} />
);
}
Both approaches do not seem to be working. Am I missing something?
With new FluentUI you can modify styles trough specific props based on button state:
export const MyButton = (props: IButtonProps) => {
const styles: IButtonStyles = {
root: {
backgroundColor: '#000000',
},
rootHovered: {
backgroundColor: '#0000ff',
},
}
return (
<PrimaryButton styles={styles} {...props} />
);
}
Codepen working example.
On this link you have IButtonStyles interface.

TypeError: theme is undefined - When trying to render Material UI component

I am having trouble rendering my react component since I separated my jss file and changed it from makeStyles to withStyles to avoid a hook problem.
I am getting an error message in my jss styling file as a couple of the methods have a 'theme' in the parenthesis for the styling to work off.
How do I go about changing this so that it renders correctly?
accessControl.component.js
import {connect, useSelector} from "react-redux";
import DataTable from "./userListTable.component";
import {Paper} from "#material-ui/core";
function AdminAccessControl(props) {
const { children, value, index, ...other } = props;
// select user object from redux
const user = useSelector(state => state.user);
// select school object from redux
const school = useSelector(state => state.diveSchool);
const classes = useStyles();
const [role, setRole] = useState({
userRole: "",
});
const handleChange = (property) => (e) => {
setRole({
// override the changed property and keep the rest
...role,
[property]: e.target.value,
});
}
return (
<div className={classes.root}>
<StyledTabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
aria-label="styled tabs example"
centered>
<StyledTab label="User Access Control" />
{/*<DataTable />*/}
<StyledTab label="Scuba School Access Control" />
{/*<DataTable />*/}
</StyledTabs>
<Typography className={classes.padding} />
</div>
);
}
function mapStateToProps(state){
const { user } = state.auth;
const { school } = state.diveSchool;
return {
user,
school,
};
}
export default compose(
connect(
mapStateToProps,
),
withStyles(useStyles)
)(AdminAccessControl);
myJss-style.js
export const useStyles = (theme) => ({
root: {
flexGrow: 1,
},
padding: {
padding: theme.spacing(3),
},
demo1: {
backgroundColor: theme.palette.background.paper,
},
demo2: {
backgroundColor: '#2e1534',
},
});
export const StyledTabs = () => ({
indicator: {
display: 'flex',
justifyContent: 'center',
backgroundColor: 'transparent',
'& > span': {
maxWidth: 40,
width: '100%',
backgroundColor: '#635ee7',
},
},
})((props) => <StyledTabs {...props} TabIndicatorProps={{ children: <span /> }} />);
export const StyledTab = (theme) => ({
root: {
textTransform: 'none',
color: '#fff',
fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.pxToRem(15),
marginRight: theme.spacing(1),
'&:focus': {
opacity: 1,
},
},
})((props) => <StyledTab disableRipple {...props} />);

How to change the progress bar background color dynamically in react material ui?

//Component Style:
const BorderLinearProgress = withStyles(theme => ({
bar: {
borderRadius: 8,
backgroundColor: "red"
}
}))(LinearProgress);
//Component use:
<BorderLinearProgress variant="determinate" value={50} />
I am new to react and material-ui.
In the above code I need to pass or change bar:backgroundColor dynamically.
Please let me know what are the options to do.
Thanks in advance
You can pass your color with the theme variable.
// Passing theme
const useStyles = makeStyles((theme) => ({
bar: props => ({
borderRadius: 8,
backgroundColor: props.color
})
}))
//Using style in component
...
const [progressColor, setProgressColor] = React.useState({ color: 'red' })
const classes = useStyles(progressColor);
// Update color based on your requirements i.e. setProgressColor({color: 'green'}) in some useEffect() when progress crosses some threshold
return (
<LinearProgress color={classes.bar} />
)
...
You can find an example in official docs: https://material-ui.com/styles/basics/#adapting-based-on-props
Below code works fine with dynamic values and colors
const LinearProgressBar: React.FC<ILinearProps> = ({ value, color }) => {
const useStyles = makeStyles({
root: {
height: 10,
borderRadius: 5
},
colorPrimary: {
backgroundColor: '#E9E9E9'
},
bar: {
borderRadius: 5,
backgroundColor: color
}
});
const classes = useStyles();
return (
<LinearProgress
variant="determinate"
value={value}
classes={{
root: classes.root,
colorPrimary: classes.colorPrimary,
bar: classes.bar
}}
/>
);
};
export default LinearProgressBar;
You can do it in two ways:
1). just Write
<LinearProgress style={{backgroundColor: "red"}} variant="determinate" value={50} />
2).
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
const styles = {
LinerProgressColor: {
backgroundColor: 'red',
},
};
function BorderLinearProgress (props) {
return <LinearProgress className={LinerProgressColor} variant="determinate" value={50} />;
}
export default withStyles(styles)(BorderLinearProgress);

How to pass className style to sub component in `material-ui`?

Material UI uses className for stying. But how can I pass the style to sub react component?
Below is my style definition.
const styles = createStyles({
root: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
},
subComponentStyle: {
...
}
});
And I use this like:
...
const NavigationBar = (props) => {
const { classes } = props;
return (
<div className={classes.root}>
// Add other code here
<SubComponent ... > // how to pass `classes.subComponentStyle` style here
</div>
)
}
...
export default withStyles(styles)(NavigationBar);
If the SubComponent component is also exported with withStyles. How can I pass some styles to override its own styling?
My SubComponent is exported as:
const styles = createStyles({
...
});
const SubComponent = ({classes}) => {
...
}
export default withStyles(styles)(SubComponent);
as you can see, it has its own classes. I don't want to override its classes completely. Is there a way to merge the passed in classes with its internal classes?
// Edited to merged styles
MUI will merge styles if you pass the classes as well as wrap the child withStyles. ie:
import { styles } from './NavStyles'
const NavigationBar = (props) => {
const { classes } = props;
return (
<div className={classes.root}>
<SubComponent classes={classes} >
</div>
)
};
export default withStyles(styles)(NavigationBar);
and in then also apply styles to the child component
import { styles } from './SubCompStyles'
const SubComponent = ({classes}) => {
// classes object is a merge of both parent and child styles
// ... component logic
};
export default withStyles(styles)(SubComponent)
Heres how you can do it with hook API:
Sub component
const useStyles = makeStyles((theme) => ({
root: {
borderRadius: 3,
color: 'white',
padding: '0 30px',
width: '12em',
height: 43,
borderRadius: 21.5,
textTransform: 'capitalize',
... your styles here.
},
}))
export default function AuthSecondaryButton(props) {
const classes = useStyles()
console.log('s', props.className)
return (
<Button
{...props}
className={clsx({
[classes.root]: true,
[props.className]: true,
})}
/>
)
}
Parent component
const useStyles = makeStyles((theme) => ({
secondaryButton: {
marginTop: theme.spacing(1),
},
}))
export default function App(props) {
const classes = useStyles()
return(
<AuthSecondaryButton
onClick={onClickSecondaryButton}
className={classes.secondaryButton}
>
Sign Up
</AuthSecondaryButton>
)
A slight tweak to #clever_usernames approach.
This uses classnames package instead of the clsx package, which we use in our project.
Replacing this...
className={clsx({
[classes.root]: true,
[props.className]: true,
})}
with this...
className={classNames(classes.root, props.className)}
Full Example
Sub component
import classNames from 'classnames'
const useStyles = makeStyles((theme) => ({
root: {
borderRadius: 3,
color: 'white',
padding: '0 30px',
width: '12em',
height: 43,
borderRadius: 21.5,
textTransform: 'capitalize',
... your styles here.
},
}))
export default function AuthSecondaryButton(props) {
const classes = useStyles()
console.log('s', props.className)
return (
<Button
{...props}
className={classNames(classes.root, props.className)}
/>
)
}
Parent component
const useStyles = makeStyles((theme) => ({
secondaryButton: {
marginTop: theme.spacing(1),
},
}))
export default function App(props) {
const classes = useStyles()
return(
<AuthSecondaryButton
onClick={onClickSecondaryButton}
className={classes.secondaryButton}
>
Sign Up
</AuthSecondaryButton>
)

Resources