Creating themeable react component with JSS - reactjs

I'm having some trouble following the docs on how to get this to work
I have a component that has some styles created, for the most part, all of these styles will apply to wherever this component is rendered but sometimes depending on what page the app is routed to I will want to change the overall height of the component on those pages. I figured I could just do this by passing in some new height props of some sort to follow a more theme centric approach. How would I get this to work with my current setup?
The styles file
const styles = theme => ({
root: {
backgroundSize: 'cover',
padding: '25px 20px',
boxSizing: 'border-box',
backgroundPosition: '50% 0',
backgroundColor: 'rgba(40,70,94,.7)',
backgroundBlendMode: 'multiply',
...theme.root
}
});
export default styles;
The component file
import React from 'react';
import styles from './EventTop.styles';
import injectSheet, { ThemeProvider } from 'react-jss';
const EventTop = (props) => (
<ThemeProvider theme={props.theme}>
<aside className={props.classes.root} style={{ backgroundImage: `url(${props.event.event_logo})` }}>
<div className="wrapper">
<div className="event-info">
<span className="event-time">
7:00 PM
</span>
<span className="event-date">
27 Jun 2020
</span>
<span className="event-end-time">
Ends at 10:00 PM
</span>
<span className="event-title">
Bidr Gala
</span>
<span className="event-attire">
Cocktail Attire
</span>
</div>
</div>
</aside>
</ThemeProvider>
);
export default injectSheet(styles)(EventTop);
And then when I render it I pictured something like this
<EventTop event={this.props.events.currentEvent} theme={{ height: 'calc(100vh - 64px)' }}/>
But when I try to console log out the theme getting passed to the style generator I don't see the theme coming in.
I have Material UI installed and the whole app is wrapped in it's MuiThemeProvider
<Provider store={store}>
<MuiThemeProvider theme={theme}>
<App>
<Reboot />
<AppRouter />
</App>
</MuiThemeProvider>
</Provider>
And this is the theme I'm actually seeing the material theme and not my own merged anywhere in it.

Here is how I got this to work
First I moved the stateless component into a new file
EventTop.component.jsx
import React from 'react';
import Moment from 'react-moment';
import styles from './EventTop.styles';
import themableWrapper from '../Themable';
const EventTopComponent = (props) => (
<aside className={props.classes.root} style={{ backgroundImage: `url(${props.event.event_logo})` }}>
<div className="wrapper">
<div className={props.classes.eventInfo}>
<span className={props.classes.eventTime}>
<Moment parse="HH:mm:ss" format="h:mm A">{props.event.start_time}</Moment>
</span>
<span className={props.classes.eventDate}>
<Moment parse="YYYY-MM-DD" format="D MMM YYYY">{props.event.event_date}</Moment>
</span>
<span className={props.classes.eventEndTime}>
Ends at <Moment parse="HH:mm:ss" format="h:mm A">{props.event.end_time}</Moment>
</span>
<span className={props.classes.eventTitle}>
{props.event.auction_title}
</span>
<span className={props.classes.eventAttire}>
{props.event.attire}
</span>
</div>
</div>
</aside>
);
export default themableWrapper(styles)(EventTopComponent);
Then in the EventTop.jsx I import the stateless component and return another stateless component but this one is wrapped in a new themable wrapper
import React from 'react';
import EventTopComponent from './EventTop.component';
import { Themable } from '../Themable';
const EventTop = ({ theme, ...rest }) => (
<Themable theme={theme}>
<EventTopComponent {...rest} />
</Themable>
);
export default EventTop;
Inside the new themable file, I have some exports setup
import injectSheet, { createTheming } from 'react-jss';
export const theming = createTheming('__NEW_THEME_NAMESPACE__');
export const { ThemeProvider: Themable } = theming;
export default (styles) => injectSheet(styles, { theming });
And then in the style generator, I can use something like lodash to merge my incoming theme with the default styles setup
EventTop.styles.js
import { merge } from 'lodash';
const styles = theme => {
return merge({
root: {
backgroundSize: 'cover',
padding: '25px 20px',
boxSizing: 'border-box',
backgroundPosition: '50% 0',
backgroundColor: 'rgba(40,70,94,.7)',
backgroundBlendMode: 'multiply',
},
eventInfo: {
flexGrow: '1',
display: 'flex',
flexDirection: 'column',
justifyContent: 'flex-end',
textAlign: 'left',
paddingTop: '20px',
fontWeight: '700',
color: 'white'
},
eventTime: {
fontSize: '55px'
},
eventDate: {
fontSize: '43px'
},
eventEndTime: {
fontSize: '30px'
},
eventTitle: {
fontSize: '70px'
},
eventAttire: {
fontSize: '30px'
}
}, theme);
};
export default styles;
In order for this to work with MuiThemeProvider wrapping the whole app I have to set up a new namespace, and then use the createTheme to make a new theme provider instance. This then needs to wrap the actual component code which is why I opted to create essentially a "container" component and the actual component as separate pieces so I could keep the logic separate. Plus this still allows me to make one stateless and the other not if I so choose

You are confusing MuiThemeProvider and the one from react-jss.

Related

Is makeStyles still able to be used in MUI v5? [duplicate]

I have a styles.tsx file because I rather not put the styling in my components:
//styles.tsx
import {grey, purple} from '#mui/material/colors';
import {styled, Theme} from '#mui/material/styles';
import {createStyles, makeStyles} from '#mui/styles';
export const drawerStyles = makeStyles((theme: Theme) =>
createStyles({
logo: {
'fontSize': '2em',
'fontFamily': 'Oleo Script Swash Caps',
'background': 'transparent',
'border': 'none',
'marginLeft': theme.spacing(1),
'width': '41px',
'overflow': 'hidden',
'transition': 'all 0.1s ease-out',
'cursor': 'pointer',
'&:hover': {
color: purple[700],
},
},
slide: {
paddingLeft: '8px',
width: '100%',
},
}),
);
In another component I import the drawerStyles:
// drawerContainer.tsx
import {drawerStyles} from 'Src/styles';
export const DrawerContainer = () => {
const classes = drawerStyles();
return (
<Box className={`${classes.logo}>
<p>some text</p>
</Box>
)
The code compiles but the browser is returning an error:
MUI: The styles argument provided is invalid. You are providing a function without a theme in the context. One of the parent elements needs to use a ThemeProvider.
On my index.tsx I use ThemeProvider:
// index.tsx
import {ThemeProvider, StyledEngineProvider} from '#mui/material/styles';
import {theme} from 'Src/theme';
const Root = () => {
return (
<ApolloProvider client={client}>
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<CssBaseline>
<App />
</CssBaseline>
</ThemeProvider>
</StyledEngineProvider>
</ApolloProvider>
);
};
render(<Root />, document.getElementById('root'));
I think the issue is because styles.tsx isn't inside the index.tsx scope. So it doesn't have the themeprovider context when the browser loads the file.
Should I just move all the styling in the component? Or is there another way?
// edit //
Created the styling with the styled api:
// styles.tsx
export const Logo = styled(Box)(({theme}) => ({
'fontSize': '2em',
'fontFamily': 'Oleo Script Swash Caps',
'background': 'transparent',
'border': 'none',
'marginLeft': theme.spacing(1),
'width': '41px',
'overflow': 'hidden',
'transition': 'all 0.1s ease-out',
'cursor': 'pointer',
'&:hover': {
color: purple[700],
},
}));
// DrawerContainer.tsx
<Link data-cy='btn_home' to='/'>
<Logo component='button'>Movieseat</Logo>
</Link>
I kind of dislike this syntax, it's not clear what type of object Logo is.
See this answer for more detail. In short, makesStyles/withStyles is not the first class APIs in MUI v5 anymore and is scheduled to be removed in v6. If you use the old API, you have to add a theme yourself.
In v4 you can import makeStyles from #material-ui/core/styles without having to supply a custom theme. You can do the same in v5 but only with the new styled API that is being used by all of the MUI components internally.

MUI errors: theme.spacing is not a function [duplicate]

I have a styles.tsx file because I rather not put the styling in my components:
//styles.tsx
import {grey, purple} from '#mui/material/colors';
import {styled, Theme} from '#mui/material/styles';
import {createStyles, makeStyles} from '#mui/styles';
export const drawerStyles = makeStyles((theme: Theme) =>
createStyles({
logo: {
'fontSize': '2em',
'fontFamily': 'Oleo Script Swash Caps',
'background': 'transparent',
'border': 'none',
'marginLeft': theme.spacing(1),
'width': '41px',
'overflow': 'hidden',
'transition': 'all 0.1s ease-out',
'cursor': 'pointer',
'&:hover': {
color: purple[700],
},
},
slide: {
paddingLeft: '8px',
width: '100%',
},
}),
);
In another component I import the drawerStyles:
// drawerContainer.tsx
import {drawerStyles} from 'Src/styles';
export const DrawerContainer = () => {
const classes = drawerStyles();
return (
<Box className={`${classes.logo}>
<p>some text</p>
</Box>
)
The code compiles but the browser is returning an error:
MUI: The styles argument provided is invalid. You are providing a function without a theme in the context. One of the parent elements needs to use a ThemeProvider.
On my index.tsx I use ThemeProvider:
// index.tsx
import {ThemeProvider, StyledEngineProvider} from '#mui/material/styles';
import {theme} from 'Src/theme';
const Root = () => {
return (
<ApolloProvider client={client}>
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<CssBaseline>
<App />
</CssBaseline>
</ThemeProvider>
</StyledEngineProvider>
</ApolloProvider>
);
};
render(<Root />, document.getElementById('root'));
I think the issue is because styles.tsx isn't inside the index.tsx scope. So it doesn't have the themeprovider context when the browser loads the file.
Should I just move all the styling in the component? Or is there another way?
// edit //
Created the styling with the styled api:
// styles.tsx
export const Logo = styled(Box)(({theme}) => ({
'fontSize': '2em',
'fontFamily': 'Oleo Script Swash Caps',
'background': 'transparent',
'border': 'none',
'marginLeft': theme.spacing(1),
'width': '41px',
'overflow': 'hidden',
'transition': 'all 0.1s ease-out',
'cursor': 'pointer',
'&:hover': {
color: purple[700],
},
}));
// DrawerContainer.tsx
<Link data-cy='btn_home' to='/'>
<Logo component='button'>Movieseat</Logo>
</Link>
I kind of dislike this syntax, it's not clear what type of object Logo is.
See this answer for more detail. In short, makesStyles/withStyles is not the first class APIs in MUI v5 anymore and is scheduled to be removed in v6. If you use the old API, you have to add a theme yourself.
In v4 you can import makeStyles from #material-ui/core/styles without having to supply a custom theme. You can do the same in v5 but only with the new styled API that is being used by all of the MUI components internally.

How to use MUI v5 makeStyles without ThemeProvider?

I have a styles.tsx file because I rather not put the styling in my components:
//styles.tsx
import {grey, purple} from '#mui/material/colors';
import {styled, Theme} from '#mui/material/styles';
import {createStyles, makeStyles} from '#mui/styles';
export const drawerStyles = makeStyles((theme: Theme) =>
createStyles({
logo: {
'fontSize': '2em',
'fontFamily': 'Oleo Script Swash Caps',
'background': 'transparent',
'border': 'none',
'marginLeft': theme.spacing(1),
'width': '41px',
'overflow': 'hidden',
'transition': 'all 0.1s ease-out',
'cursor': 'pointer',
'&:hover': {
color: purple[700],
},
},
slide: {
paddingLeft: '8px',
width: '100%',
},
}),
);
In another component I import the drawerStyles:
// drawerContainer.tsx
import {drawerStyles} from 'Src/styles';
export const DrawerContainer = () => {
const classes = drawerStyles();
return (
<Box className={`${classes.logo}>
<p>some text</p>
</Box>
)
The code compiles but the browser is returning an error:
MUI: The styles argument provided is invalid. You are providing a function without a theme in the context. One of the parent elements needs to use a ThemeProvider.
On my index.tsx I use ThemeProvider:
// index.tsx
import {ThemeProvider, StyledEngineProvider} from '#mui/material/styles';
import {theme} from 'Src/theme';
const Root = () => {
return (
<ApolloProvider client={client}>
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<CssBaseline>
<App />
</CssBaseline>
</ThemeProvider>
</StyledEngineProvider>
</ApolloProvider>
);
};
render(<Root />, document.getElementById('root'));
I think the issue is because styles.tsx isn't inside the index.tsx scope. So it doesn't have the themeprovider context when the browser loads the file.
Should I just move all the styling in the component? Or is there another way?
// edit //
Created the styling with the styled api:
// styles.tsx
export const Logo = styled(Box)(({theme}) => ({
'fontSize': '2em',
'fontFamily': 'Oleo Script Swash Caps',
'background': 'transparent',
'border': 'none',
'marginLeft': theme.spacing(1),
'width': '41px',
'overflow': 'hidden',
'transition': 'all 0.1s ease-out',
'cursor': 'pointer',
'&:hover': {
color: purple[700],
},
}));
// DrawerContainer.tsx
<Link data-cy='btn_home' to='/'>
<Logo component='button'>Movieseat</Logo>
</Link>
I kind of dislike this syntax, it's not clear what type of object Logo is.
See this answer for more detail. In short, makesStyles/withStyles is not the first class APIs in MUI v5 anymore and is scheduled to be removed in v6. If you use the old API, you have to add a theme yourself.
In v4 you can import makeStyles from #material-ui/core/styles without having to supply a custom theme. You can do the same in v5 but only with the new styled API that is being used by all of the MUI components internally.

Hook API Styles aren't loading?

The project is being built with Next.js and material-ui. I've used the Hook API method to style my components but the styles aren't always loading. When I run next dev to test it, my styles don't load. If I edit anything in the page currently being rendered, it reloads, but this time with the styles. If I click reload, however, the styles fail to load again. I've been searching for help with this but I can't find anything that seems to solve the issue. I saw a few things about using material-ui/core with material-ui/styles but it's all in reference to v3 and it doesn't solve the issue I have.
Here's one of the pages for example:
import Nav from '../components/nav';
import Footer from '../components/footer';
import Product from '../components/product';
import { makeStyles, ThemeProvider } from '#material-ui/core/styles';
import theme from '../components/customTheme';
import {
Typography,
Button
} from '#material-ui/core';
const useStyles = makeStyles({
body: {
margin: '15vh 0',
backgroundColor: ' white',
textAlign: 'left'
},
earnWrapper: {
display: 'block',
textAlign: 'left',
backgroundColor: 'white',
width: '80%',
margin: '5% 2vh',
padding: '5%',
borderRadius: '25px',
transition: '0.3s',
boxShadow: '0px 5px 20px #dedede'
},
earnContent: {
textAlign: 'left',
display: 'inline-block'
},
earntCaption: {
color: 'grey',
},
earntAmount: {
margin: '1vh 0 2vh'
},
shareInfo: {
margin: '5% 2vh',
textAlign: 'left'
},
products: {
textAlign: 'center ',
width: '100%'
}
});
export default function Home(props) {
const styles = useStyles();
return (
<ThemeProvider theme={theme}>
<DefaultHead
title="Oorigin | Home"
/>
<Nav isLoggedIn={true} />
<div className={styles.body}>
<div className={styles.earnWrapper}>
<div className={styles.earnContent}>
<Typography className={styles.earntCaption} variant="caption">You've earned</Typography>
<Typography className={styles.earntAmount} variant="h4">S$18.50</Typography>
<Button disableElevation variant="contained" color="primary">Withdraw</Button>
</div>
</div>
<div className={styles.shareInfo}>
<Typography><b>Shop, Share, Earn</b></Typography>
<Typography><br/>Shop products you like, share products you love, and earn up to 10% commission on every qualifying sale you refer</Typography>
</div>
<div className={styles.products}>
<Product
imgURL="../TestItem1.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
link="/products/staticProduct"
/>
<Product
imgURL="../TestItem2.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
/>
<Product
imgURL="../TestItem3.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
/>
<Product
imgURL="../TestItem4.svg"
imgAlt="Test Product"
title="Disinfecting Air Purifying Solution"
price={(22.80).toFixed(2)}
/>
</div>
</div>
<Footer/>
</ThemeProvider>
);
}

External Css not working in Gatsby react project

I am not sure if this is the correct way of using CSS in gatsby but for some reason my external syles are not being applied in gatsby project.
This is what I am doing it
import { Link } from "gatsby"
import PropTypes from "prop-types"
import React from "react"
import "./header.css"
const Header = (props) => {
return (
<header
style={{
background: `black`,
marginBottom: `1.45rem`,
}}
>
<div
style={{
margin: `0 auto`,
maxWidth: 960,
padding: `1.45rem 1.0875rem`,
}}
>
<h1 style={{ margin: 0 }}>
<Link
to="/"
style={{
color: `white`,
textDecoration: `none`,
}}
>
{props.siteTitle} <span className="header-description"> {props.description} </span>
</Link>
</h1>
</div>
</header>
)
}
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: ``,
}
export default Header
and this is my header.css
.header-description {
font-size: 12;
}
Your css is invalid. font-size can't have unitless values except for 0.
Maybe you wanted it to be 12px?
what i usually do is create and import index.css into layer component (or any other wrapping component) aaaand then import individual/uniques/modules to that index.css. So the main index.css becomes sort of like a hub for css files which then is being imported only in once place. well, obviously if you would use css modules you would have to import them into your react components, but by the looks of it you are just using classnames. so...yea, try my method, maybe that helps? Theres also like 4 tutorials on how to import and use css and various styling methodologies in gatsby docs.

Resources