How to apply custom theme in material ui - reactjs

I am trying to apply a custom theme to my React component after reading this tutorial
http://www.material-ui.com/#/customization/themes
I wrote my theme in a separate javascript file like this
import Colors from 'material-ui/lib/styles/colors';
import ColorManipulator from 'material-ui/lib/utils/color-manipulator';
import Spacing from 'material-ui/lib/styles/spacing';
import zIndex from 'material-ui/lib/styles/zIndex';
export default {
spacing: Spacing,
zIndex: zIndex,
fontFamily: 'Roboto, sans-serif',
palette: {
primary1Color: Colors.cyan500,
primary2Color: Colors.cyan700,
primary3Color: Colors.lightBlack,
accent1Color: Colors.pinkA200,
accent2Color: Colors.grey100,
accent3Color: Colors.grey500,
textColor: Colors.deepPurpleA700,
alternateTextColor: Colors.white,
canvasColor: Colors.white,
borderColor: Colors.grey300,
disabledColor: ColorManipulator.fade(Colors.darkBlack, 0.3),
pickerHeaderColor: Colors.cyan500,
}
};
I apply this theme to my component in the follow way
import React from 'react';
import mui from 'material-ui';
import injectTapEventPlugin from 'react-tap-event-plugin';
import ThemeManager from 'material-ui/lib/styles/theme-manager';
import Colors from 'material-ui/lib/styles/colors';
import MyTheme from './theme.js';
injectTapEventPlugin();
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
messages : [{id: 1, text: 'Hi'}, {id: 2, text: 'Hello'}]
};
}
getChildContext() {
return {
muiTheme: ThemeManager.getMuiTheme(MyTheme)
};
}
componentWillMount() {
let newMuiTheme = this.state.muiTheme;
this.setState({
muiTheme: newMuiTheme,
});
}
render() {
var messageNodes = this.state.messages.map((message) => {
return (<div key={message.id}>{message.text}</div>);
});
return (<div>{messageNodes}</div>);
}
}
App.childContextTypes = {
muiTheme: React.PropTypes.object
};
export default App;
According to my theme when my control renders it should have a "deepPurpleA700" color .... but my control text is always black. So my theme is not applied.
My full code is available at https://github.com/abhitechdojo/MovieLensReact

You should first import your color from 'material-ui/styles/colors'
and then use them in palette object like this :
import React, { Component } from 'react';
import {render} from 'react-dom';
import {indigo500, indigo700, redA200} from 'material-ui/styles/colors';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
import Main from './Main';
const muiTheme = getMuiTheme({
palette: {
primary1Color: indigo500,
primary2Color: indigo700,
accent1Color: redA200,
pickerHeaderColor: indigo500,
},
});
class App extends Component {
render() {
return (
<div>
<MuiThemeProvider muiTheme={muiTheme}>
<Main />
</MuiThemeProvider>
</div>
);
}
}
render(<App/>, document.getElementById('app')); //you should be having a div with an id of app in your index.html file
and the Main.js file is
import React, { Component } from 'react';
import FloatingActionButton from 'material-ui/FloatingActionButton';
export default class Main extends Component {
render() {
return (
<div>
<AppBar title="Title"/>
<FloatingActionButton secondary={true} />
</div>
);
}
}

For me it worked with:
import React, {Component} from 'react';
import {createMuiTheme} from '#material-ui/core/styles';
import MuiThemeProvider from '#material-ui/core/styles/MuiThemeProvider';
const theme = createMuiTheme({
palette: {
primary: {
main: '#0b5994',
},
secondary: {
main: '#1d83c6',
},
},
});
class App extends Component {
render() {
return (
<div className="App">
<MuiThemeProvider theme={theme}>
/* content here! */
</MuiThemeProvider>
</div>
);
}
}
export default App;

I am pretty sure you need to have
static childContextTypes = {
muiTheme: React.PropTypes.object,
};
before your
getChildContext()
method. Once that is done you should be able to remove any theme related stuff from
componentWillMount()
Now, this won't work for base text. But I can confirm that the theme is being applied. I tested it by adding an appBar component and changing the color in your theme.js file. I also added a List with ListItems and that text style is what you are looking for.
Here is a link to the gist of your modified App.jsx file.
Also, as a side note in you server.js you have a small typo on line 5 you should have
new webpackDevServer(webpack(config), {
not
new WebpackDevServer(webpack(config), {

Related

Material UI: Use theme in React Class Component

I am looking for something like ThemeConsumer (which probably doesn't exist). I've React component and I am using withStyles() higher-order component to inject custom styles. It's pretty well described in documentation but I didn't find any example which uses theme.
I have some base component which contains ThemeProvider. It means any of MUI components are being affected by it.
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const theme = getTheme(prefersDarkMode);
return (
<ThemeProvider theme={theme}>
...
</ThemeProvider>
)
I also use some functional components with makeStyles() to create styles with provided theme.
const useStyles = makeStyles(theme => ({
// here I can use theme provided by ThemeProvider
});
But it can't be used in class components. So I am using withStyles() HOC.
const styles = {
// I would like to use here provided theme too
}
export default withStyles(styles)(SomeComponent);
Summary of my question:
How do I use provided theme in class component?
withStyles supports similar syntax as makeStyles:
const styles = theme => ({
// here I can use theme provided by ThemeProvider
});
export default withStyles(styles)(SomeComponent);
Here's a simple working example:
import React from "react";
import { withStyles } from "#material-ui/core/styles";
import Paper from "#material-ui/core/Paper";
const StyledPaper = withStyles(theme => ({
root: {
backgroundColor: theme.palette.secondary.main
}
}))(Paper);
export default function App() {
return (
<StyledPaper className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</StyledPaper>
);
}
A component decorated with withStyles(styles) gets a special classes prop injected, which may be used for your custom styles. For example:
import { Box } from "#material-ui/core"
import { withStyles } from "#material-ui/core/styles"
const styles = theme => ({
myCustomClass: {
color: theme.palette.tertiary.dark
}
})
class myComponent extends Component {
render() {
const { classes, theme } = this.props
// In last line, you see we have passed `{ withTheme: true }` option
// to have access to the theme variable inside the class body. See it
// in action in next line.
return <Box className={classes.myCustomClass} padding={theme.spacing(4)} />
}
}
export default withStyles(styles, { withTheme: true })(myComponent)
If you pass { withTheme: true } option to withStyles HOC, you'll get the theme variable injected as a prop also.
If you have other HOCs (e.g. Redux's connect, Router, etc) applied to your component, you may use it like this:
export default withStyles(styles, { withTheme: true })(
withRouter(connect(mapStateToProps)(myComponent))
)
A more comprehensive explanation for this topic could be found in this article: Using Material UI theme variable in React Function and Class Components.
Use withTheme HOC
modified example from docs
import { withTheme } from '#material-ui/core/styles';
class DeepChildRaw
{
/*...*/
render()
{
return <span>{`spacing ${this.props.theme.spacing}`}</span>;
}
}
const DeepChild = withTheme(DeepChildRaw);
if are working with Class Components, you can use like here ;)
import React from 'react';
import Routes from './Routes';
import '../custom.css';
import { BrowserRouter } from 'react-router-dom';
import { MuiThemeProvider, createTheme } from '#material-ui/core/styles';
const theme = createTheme({
palette: {
primary: {
main: '#fff'
},
secondary: {
main: '#351436'
}
}
});
class App extends React.Component {
render() {
return (
<div className="App">
<MuiThemeProvider theme={theme}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</MuiThemeProvider>
</div>
)
}
}
export default App;

What is best practice of 'ThemeProvider' in styled-components?

I wanna know where <ThemeProvider/> should be placed in React app.
I'd come up with two solutions about it.
1, <ThemeProvider/> should be used 'Just Once' in top-root component
like index.js or App.js file created by 'create-react-app' tool.
2, <ThemeProvicer/> should be placed in 'Each root of React-component'
literally.
for clarification, I'll show you some example.
there is just two component, 'Red' and 'Blue' <div> tag.
1, <ThemeProvider/> used 'Just Once'
// In './red.js'
import React from 'react'
import styled from "styled-components"
const Red = styled.div`background: ${props => props.theme.mainColor}`
export default function RedDiv() {
return (
//NOT using ThemeProvider
<Red />
)
}
// In './blue.js'
......
const Blue = styled.div`background: ${props => props.theme.subColor}`
export default function BlueDiv() {
return (
<Blue />
)
}
// In './App.js'
import React, { Component } from 'react'
import { ThemeProvider } from "styled-components"
import myTheme from "./myTheme
import Red from "./red"
import Blue from "./blue"
export default class App extends Component {
render() {
return (
//only used here just once
<ThemeProvider theme={myTheme}>
<>
<Red />
<Blue />
</>
</ThemeProvider>
)
}
}
2, <ThemeProvider/> used 'Each root of React-component'
// In './red.js'
import React from 'react'
import styled, { ThemeProvider } from "styled-components"
const Red = styled.div`background: ${props => props.theme.mainColor} `
export default function RedDiv() {
return (
<ThemeProvider theme={myTheme}>
<Red />
</ThemeProvider>
)
}
// In './blue.js'
......
const Blue = styled.div`background: ${props => props.theme.mainColor}`
export default function BlueDiv() {
return (
<ThemeProvider theme={myTheme}>
<Blue />
</ThemeProvider>
)
}
// In './App.js'
import React, { Component } from 'react'
import Red from "./red"
import Blue from "./blue"
export default class App extends Component {
render() {
return (
<>
// <ThemeProvider/> is not used
<Red />
<Blue />
</>
)
}
}
there is maybe some typo on the code above, but I hope that this example will convey my idea clearly.
I use it only once, inside index.js.
Also a good place to add some global styles, if you need them. I use them for resetCSS (http://meyerweb.com/eric/tools/css/reset/) and some baseCSS rules like box-sizing etc.
index.js
import { createGlobalStyle, ThemeProvider } from 'styled-components';
import theme from './styles/theme';
import resetCSS from './styles/resetCSS';
import baseCSS from './styles/baseCSS';
import { BrowserRouter as Router} from "react-router-dom";
const GlobalStyle = createGlobalStyle`
${resetCSS}
${baseCSS}
`;
React.DOM.render(
<React.Fragment>
<GlobalStyle/>
<Router>
<ThemeProvider theme={theme}>
<App/>
</ThemeProvider>
</Router>
</React.Fragment>
,document.getElementById('root')
);

how to snapshot from component created with material ui styles in way that styles would be in snapshot

I am using material-UI in my react web app.
I want to test my component with jest in way that styles would be saved in snapshot of component like what jest-styled-component do for styled-component but I cant.
you can see my sample project in codesandbox
but here are the important parts:
I create my project with create-react-app with this command:
npx create-react-app myapp
this is my index.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {MuiThemeProvider} from '#material-ui/core/styles';
import theme from './customTheme';
ReactDOM.render(
<MuiThemeProvider theme={theme}>
<App/>
</MuiThemeProvider>,
document.getElementById('root')
);
this is my customTheme.js file in src folder:
import {createMuiTheme} from '#material-ui/core/styles';
const theme = createMuiTheme({
typography: {
fontFamily: [
'Roboto',
'"Segoe UI"',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
},
palette: {
color: 'blue',
}
});
export default theme;
and this is my App.js that I want to test:
import React from 'react';
import PropTypes from 'prop-types';
import {withStyles} from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
export const styles = theme => ({
mystyle: {
fontFamily: theme.typography.fontFamily,
border: '1px solid green',
background: 'yellow',
color: theme.palette.color,
},
});
function MyComponent(props) {
const {classes} = props;
return (
<div>
<p className={classes.mystyle}> my text </p>
<Button className={classes.mystyle}>my button</Button>
</div>
);
}
MyComponent.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(MyComponent);
please notice that I am overwriting styles with JSS and I have my own theme.
I want to get snapshot from component in way that css be part of snapshot like this issue from material-UI's repository in github or like what jest-styled-component-do for styled-component. for reaching that goal I wrote this code in my demo.test.js:
import React from "react";
import renderer from "react-test-renderer";
import App from '../App';
import {styles} from '../App';
describe("snapshot test", () => {
it("should match snapshot", () => {
const wrapper = renderer
.create(<App classes={styles}/>)
.toJSON();
expect(wrapper).toMatchSnapshot();
});
});
but with this test my styles are not in snapshot therefore if I change mystyle in App component the snapshot test will not fail.
I have read bellow resources but couldn't get what I wanted:
https://github.com/mui-org/material-ui/issues/9492
https://github.com/mui-org/material-ui/issues/6834
thanks for any feedback

Composition of react-emotion and external css (eg. bootstrap) classes

Is there a way to add external (eg. bootstrap) classes along with react-emotion styling?
import React, { Component } from 'react';
import { css } from 'react-emotion';
const MyStyle = css`
STYLE
`
export default class MyComponent extends Component {
render() {
return (<button className={css`${MyStyle}` /* add in some way `btn btn-default` */ }>Text</button>);
}
}
Thanks!
Yes you can. Below is the link where i had made a small example, the font colour is coming from react-emotion and background colour is coming from bootstrap.
import React, { Component } from 'react';
import { render } from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import styled, { css } from 'react-emotion'
const myStyle = css`
color: rebeccapurple;
`;
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div className={myStyle + ' bg-primary'}>Hello World</div>
);
}
}
render(<App />, document.getElementById('root'));

MuiThemeProvider: How to use different themes for different routes?

I need to slightly change the theme depending on the current section of the site.
It seems that MuiThemeProvider only sets muiTheme on load; but it needs to be updated when the props change.
How can this be done?
You can try to put the theme in a wrapping component that keeps the theme in it's state. Using React's context this component exposes a function to child components to change the state.
import React, { Component } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import PropTypes from 'prop-types';
import theme from './theme';
import themeOther from './theme-other'
class Wrapper extends Component {
static childContextTypes = {
changeTheme: PropTypes.func
};
constructor(props) {
super(props);
this.state = {
muiTheme: getMuiTheme(theme)
};
}
getChildContext() {
return {changeTheme: this.changeTheme};
}
changeTheme = () => {
this.setState({
muiTheme: getMuiTheme(themeOther)
})
};
render() {
return (
<MuiThemeProvider muiTheme={this.state.muiTheme}>
{this.props.children}
</MuiThemeProvider>
)
}
}
export default Wrapper;
In some child component you can access the context and call the changeTheme function to set a different theme in the state. Make sure to include contextTypes else you can't access the function.
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import PropTypes from 'prop-types'
class ChildComponent extends Component {
static contextTypes = {
changeTheme: PropTypes.func
};
render() {
return (
<RaisedButton
primary
onTouchTap={this.context.changeTheme}
label="Change The Theme"
/>
);
}
}
export default ChildComponent;
In the root of your app just render the wrapper.
ReactDOM.render(
<Wrapper>
<App />
</Wrapper>,
document.getElementById('root')
);
EDIT:
My first solution might have been abit too much. Since you are replacing the whole theme for the whole app. You can also use the MuiThemeProvider down the tree like so.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import inject from 'react-tap-event-plugin';
inject();
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import theme from './theme';
ReactDOM.render(
<MuiThemeProvider muiTheme={getMuiTheme(theme)}>
<App />
</MuiThemeProvider>,
document.getElementById('root')
);
In a child component you can just use the MuiThemeProvider again and override some properties. Note that these changes will reflect on all the children inside this MuiThemeProvider.
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import theme from './theme';
import { green800, green900 } from 'material-ui/styles/colors';
const localTheme = getMuiTheme(Object.assign({}, theme, {
palette: {
primary1Color: green800,
primary2Color: green900
}
}));
class App extends Component {
render() {
return (
<div>
<RaisedButton
primary
label="Click"
/>
<MuiThemeProvider muiTheme={localTheme}>
<RaisedButton
primary
label="This button is now green"
/>
</MuiThemeProvider>
</div>
);
}
}
export default App;

Resources