Passing styling options using JSS/Material-UI - reactjs

I've written a small wrapper component for the Paper Material-UI Component:
import React from 'react';
import Paper from '#material-ui/core/Paper';
import {withStyles} from '#material-ui/core/styles';
const styles = theme => ({
root: {
...theme.mixins.gutters(),
paddingTop: theme.spacing.unit * 2,
paddingBottom: theme.spacing.unit * 2,
},
});
const PaddedPaper = (props) => {
const {classes, children} = props;
return (
<Paper className={classes.root}>
{children}
</Paper>
);
};
export default withStyles(styles)(PaddedPaper);
Which, as you may have guessed, is used like this:
<PaddedPaper>
<p>Some content.</p>
</PaddedPaper>
With JSS, is it possible to pass padding into PaddedPaper as a prop?
<PaddedPaper padding={20}>
<p>Some content.</p>
</PaddedPaper>
Since styles is defined outside of the PaddedPaper class, and doesn't have access to props, how can I pull this off? Or am I thinking about this entire process incorrectly?

When you're using withStyles, you have access to the theme, but not props.
this is still an ongoing issue : https://github.com/mui-org/material-ui/issues/7633
easiest way to use props in your styles is using inline styles (for now)
like this:
function PaperSheet(props) {
return (
<div>
<PaddedPaper {...props} size={10}>
<Typography variant="headline" component="h3">
This is a sheet of paper.
</Typography>
<Typography component="p">
Paper can be used to build surface or other elements for your
application.
</Typography>
</PaddedPaper>
</div>
);
}
const PaddedPaper = props => {
const { children, size } = props;
console.log(size);
return <Paper style={{ padding: size }}>{children}</Paper>;
};
here is a working example: https://codesandbox.io/s/yl4671wxz

Related

Apply MUI styles to non-MUI markup elements

In my Gatsby app I am using MUI v5, and must also output user created markup. I want the user-markup to get the same base-styles as their analogous Typography elements (see below). How can I achieve this?
Note - the user markup already contains <p> <h1> or other tags, which cannot be directly modified by React.
Additionally the app is wrapped with ThemeProvider and I'm using styled-components as the style engine for MUI (if that matters...).
import {Typography} from "#mui/material"
export default function() {
return (
<>
<Typography variant="body1" paragraph>My styled Typography</Typography>
// The next line can't receive any classses or modifiers,
// but must end up styled like the <Typography> element.
<p>my custom user content - how can i style this like above?</p>
</>
)
}
You need to import your theme. From there you can access the body1 default typography style to apply to the p element.
import {Typography} from '#mui/material'
import {useTheme} from //im not exactly sure where this comes from '#mui/material' or '#mui/styles'
export default function() {
const theme = useTheme()
return (
<>
<Typography variant="body1" paragraph>My styled Typography</Typography>
<p style={theme.typography.body1}>my custom user content - how can i style this like above?</p>
</>
)
}
import {useTheme ,Typography} from "#mui/material"
export default function() {
const theme = useTheme()
return (
<>
<Typography variant="body1" paragraph>My styled Typography</Typography>
<p style={theme.typography.body1}>my custom user content - how can i style this like above?</p>
</>
)
}
If you want to add the sx prop in your custom component:
const P = styled("p")({});
const theme = useTheme()
<P sx={{ ...theme.typography.body1 }}>
If you want to use system properties:
import { unstable_extendSxProp as extendSxProp } from "#mui/system";
const Psx = styled("p")();
function P(inProps) {
const { sx, ...other } = extendSxProp(inProps);
return <Psx sx={sx} {...other} />;
}
<P {...theme.typography.body1}>
If you want to use variant prop like in Typography:
const T = styled('p')(({ theme, variant = 'body1' }) => ({
...theme.typography[variant],
}));
<T variant="h3">
Live Demo
Related Answer
Passing props to MUI styles

How do I set the onClick in the navbar of material-ui?

Below is the code for creating the navigation bar/header for an application. I have used material-ui.
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import AccountCircle from "#material-ui/icons/AccountCircle";
import ShoppingCartOutlinedIcon from "#material-ui/icons/ShoppingCartOutlined";
const styles = (theme) => ({
root: {
width: "100%",
},
flex: {
flex: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
});
class Nav extends Component {
render() {
const { classes } = this.props;
return (
<AppBar position="static" elevation={0}>
<Toolbar>
<IconButton
className={classes.menuButton}
color="contrast"
onClick={this.props.toggleDrawer}
>
<MenuIcon />
</IconButton>
<Typography className={classes.flex} type="title" color="inherit">
Pizza Shop
</Typography>
<div>
<IconButton color="contrast" onClick={this.props.cart}>
<ShoppingCartOutlinedIcon />
</IconButton>
<IconButton color="contrast" onClick={this.props.login}>
<AccountCircle />
</IconButton>
</div>
</Toolbar>
</AppBar>
);
}
}
Nav.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Nav);
I'm new to 'props' and I'm not sure what this.props.cart or this.props.login will do. I want to create a functionality where when I click on the above two icons, I'm directed to some another component, but I'm not sure how to do it. Please help me to understand it.
Props are just parameters that the parent component sent to the children component. In your example this.props.cart and this.props.login are functions ( I am not sure about the cart, but it is used as a function ). From your example, when you click on the icons, you will call cart and login functions sent from the parent.
The answer to your question "How do you set onClick" is you already doing it on the login function/method. So you need to look at the parent component implementation.
Below I wrote a much more readable example, so feel free to look
import React from 'react'
class ChildrenComponent extends React.Component {
render() {
// This is doing the same thing, just we call the function / method in little different way
// return <div onClick={() => { this.props.onShoppingSubmit() }}>Aaa</div>
return <div onClick={this.props.onShoppingSubmit}>Aaa</div>
}
}
class ParentComponent extends React.Component {
handleShoppingSubmit = () => {
// Do what every that function needs to do
console.log('Hi from parent')
}
render() {
return (
<div>
<ChildrenComponent onShoppingSubmit={this.handleShoppingSubmit} />
</div>
)
}
}

Making a Materal UI card template in react

I'm trying to make a card template in React using Material UI. I need this template so I can easily generate more cards in the future. The documentation often have the script in one single file, and never explain how to separate it and still having it to work. I don't gonna use the export import lines here just to simplify the code.
Lets say i have a Cardtemplate.jsx
const useStyles = makeStyles({
root: {
maxWidth: 345,
},
subtitle: {
fontSize: 14,
},
card:{
height: 100,
}
title:{
fontSize: 12,
}
});
const Cardtemplate = ({ classes, image, title, subtitle }) => {
return (
<Card className={classes.card}>
<CardMedia image={image} />
<Typography className={classes.title} variant={'h2'}>{title}</Typography>
<Typography className={classes.subtitle}>{subtitle}</Typography>
</Card>
);
};
export default Cardemplate;
Then I want to use the temple to generate card1.jsx
export default function Card1() {
const classes = useStyles();
return (
<Cardtemplate
classes={styles}
title={'Some title'}
subtitle={'some subtitle'}
image={'image.png'}
/>
);
}
Finally in the App.js I would render all the cards like this.
function App(){
return(
<Card1 />
<Card2 />
<Card3 />
...
...
)
};
The problem is that I getting syntaxError telling that Identifier 'useStyles' has already been declared or some undefined properties is missing. I have tried to place the const classes = useStyles(); in the cardTemplate.jsx.
I have also tried to wrap the const useStyles inside the const Cardtemplate function just to make sure that the useStyles also get exported, but all I get Is errors.
There's something I have been missing here. How can I do this the proper way without any further errors?
Your CardTemplate shouldn't receive classes from props, it should be declared on it, using the useStyles defined above, so it should look like this:
const useStyles = makeStyles({
// style
});
export default function CardTemplate({ title, subtitle }) {
const classes = useStyles();
return (
<Card className={classes.card}>
<Typography className={classes.title} variant={"h2"}>
{title}
</Typography>
<Typography className={classes.subtitle}>{subtitle}</Typography>
</Card>
);
}
Then, you can have each of your Cards importing this component and reusing, per example:
// Card1.js
export default () => {
return <CardTemplate title="Card 1" subtitle="The First" />;
};
// Card2.js
export default () => {
return <CardTemplate title="Card 2" subtitle="The Second" />;
};
// Card3.js
export default () => {
return <CardTemplate title="Card 3" subtitle="The Third" />;
};
Lastly, you can render them in any place as you seem fit, example:
ReactDOM.render(
<>
<Card1 />
<Card2 />
<Card3 />
</>,
document.querySelector("#root")
);
There is a running code sandbox I made for you. You can check it here!

Is there a way to change Typography defaults in a component?

The default variant and color of the Typography component is body1 and initial. I've made a component, and when the Typography component is used as children of it, I'd like the default to instead be body2 and textSecondary. Is there a way to do this in Material UI?
<Sidebar>
<Typography>
This should have body2 and textSecondary
when nothing else is specified.
</Typography>
</Sidebar>
<Typography>
This should have the regular defaults.
</Typography>
I can of course do the following, but would really prefer a way where the child components could still use the regular Typography component. Or if there's a way to extend/create an alternative Typography component that doesn't result in two components like here (the wrapper and the wrapped typography component).
import React from 'react';
import Typography, { TypographyProps } from 'components/Typography';
export default function SidebarTypography({
variant = 'body2',
color = 'textSecondary',
...props
}: TypographyProps): React.ReactElement {
return <Typography variant={variant} color={color} {...props} />;
}
<Sidebar>
<SidebarTypography>
This has body2 and textSecondary.
</SidebarTypography>
</Sidebar>
<Typography>
This has the regular defaults.
</Typography>
Solved using a combination of the suggested duplicate (Is it possible to override material-ui components default props?), and the "nested themes" feature (https://material-ui.com/customization/theming/#nesting-the-theme) I didn't know about.
const sidebarTheme = (theme) =>
createMuiTheme({
...theme,
props: {
...theme.props,
MuiTypography: {
color: 'textSecondary',
variant: 'body2',
},
},
});
export default function SideBar(props) {
return (
<ThemeProvider theme={sideBarTheme}>{props.children}</ThemeProvider>
);
}
You can also override globally like this any variant of MuiTypography.
import { createMuiTheme, MuiThemeProvider } from '#material-ui/core/styles';
const theme = createMuiTheme({
overrides: {
MuiTypography: {
body1: {
fontSize: '18px !important',
color: 'red',
},
},
},
});
<MuiThemeProvider theme={theme}>
// Your pages...
</MuiThemeProvider>
<Typography variant="h1" component="h2">
h1. Heading
</Typography>

Insert image into Material-UI AppBar

I've been looking for a way to insert a plain image file into the AppBar of Material-UI on the official docs but it seems like the only non-text things you can put in the AppBar are either text icons or SVG icons. Is there a way to display actual images in the AppBar?
There are several options to insert a background image for Material-UI AppBar.
Here is one that I prefer:
Import the image:
import headerBackground from "../../Assets/headerBackground.png";
Add the image to the stylesheet as follow:
header: {
backgroundImage: `url(${headerBackground})`,
},
Set the class component:
const classes = useStyles();
return (
<>
<AppBar className={classes.header}> ... </AppBar>
Here is a complete example:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import { AppBar, Toolbar, Typography } from "#material-ui/core";
import headerBackground from "../../Assets/headerBackground.png";
const useStyles = makeStyles((theme) => ({
header: {
backgroundImage: `url(${headerBackground})`,
},
}));
export default function Header(props) {
const classes = useStyles();
return (
<AppBar className={classes.header}>
<Toolbar>
<Typography variant="h6">Scroll to Hide App Bar</Typography>
</Toolbar>
</AppBar>
);
}
Material-UI has some components in which there are properties that can be assigned to the image. For example CardMedia - the properties of image. But you can also use the standard tag to insert the image.
<AppBar position="static" color="default" elevation={0} className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<img src="/assets/logo.svg" />
<nav>
<Link variant="button" color="textPrimary" href="#" className={classes.link}>
About
</Link>
</nav>
</Toolbar>
</AppBar>
It works for me.

Resources