Alternative to innerTheme when we want to have different primaryTypographyProps - reactjs

I have a component which has a List customized with ListeItems and Icons and Buttons. I want to use this component in 2 different places. In one place, I set the MuiListItemTextTypography props this way..
const theme = createMuiTheme({
.
.
.
typography: {
useNextVariants: true
body1: {
fontSize: '14px'
}
},
props: {
MuiListItemText: {
primaryTypographyProps: {
variant: "body1"
},
secondaryTypographyProps: {
variant: "body2"
}
}
}
})
But a second component wants the font size to be 12 px, so I did it this way
const innerTheme = createMuiTheme({
typography: {
body1: {
fontSize: '12px'
}
},
props: {
MuiListItemText: {
primaryTypographyProps: {
variant: "body1"
},
secondaryTypographyProps: {
variant: "body1"
}
}
}
});
and wrapped the second component in new Theme
<MuiThemeProvider theme={innerTheme}>
<ListItems itemsList={secondItem}/>
</MuiThemeProvider>
It works fine, but the drawback is that I lose all the styles set in main theme when I wrap it in an inner theme
How can I do this so that I do not lose all that in the first theme.

There is a good example of how to do this here: Nesting the Theme
MuiThemeProvider accepts a function, so you can redefine innerTheme as:
const innerTheme = {
typography: {
body1: {
fontSize: '12px'
}
},
props: {
MuiListItemText: {
primaryTypographyProps: {
variant: "body1"
},
secondaryTypographyProps: {
variant: "body1"
}
}
}
}
And then change your markup to:
<MuiThemeProvider theme={theme => createMuiTheme({...theme, ...innerTheme})}>
<ListItems itemsList={secondItem}/>
</MuiThemeProvider>

Related

React / Material-UI: Use variable reference in custom theme

Is there a way in Material-UI (React) to use a reference in a custom theme to a variable defined in the same theme? For example, the component MuiBottomNavigation should use the primary color (main) as background.
import { createTheme, useTheme } from '#mui/material/styles';
const theme = createTheme({
palette: {
primary: {
main: '#f6b1b2'
}
},
components: {
MuiBottomNavigation: {
styleOverrides: {
root: {
// This is not working
backgroundColor: {palette.primary.main}
},
}
}
}
});
You can achieve this by creating several themes and then merging them into one:
// Define all your default styles
const themeBase = createTheme({
palette: {
primary: {
main: "#f6b1b2"
}
}
});
// Define all your component styles
const themeComponents = createTheme({
components: {
MuiBottomNavigation: {
styleOverrides: {
root: {
backgroundColor: themeBase.palette.primary.main
},
}
}
}
});
const theme = createTheme({
...themeBase,
components: themeComponents.components,
});
Of course, you wouldn't need to create an extra theme for the components. You could also just insert them into the final theme.
No, you can't. there is no such API to access the theme when you define it. So something like this wouldn't work
styleOverrides: {
root: {
backgroundColor: theme => theme.palette.primary.main
},
}
To workaround this you can put the commonly shared value into a variable and reference it:
const primaryMain = '#f6b1b2';
const theme = createTheme({
palette: {
primary: {
main: primaryMain,
}
},
components: {
MuiBottomNavigation: {
styleOverrides: {
root: {
backgroundColor: primaryMain,
}
}
}
}
});
If you only need to access a default theme variable, you can create one and reference it like this:
const defaultTheme = createTheme();
const theme = createTheme({
components: {
MuiBottomNavigation: {
styleOverrides: {
root: {
backgroundColor: defaultTheme.palette.primary.main,
}
}
}
}
});

Material UI doesn't use overridden styles when using useTheme hook

I'm trying to use custom theming in Material UI like so:
const theme = createMuiTheme({
palette: {
primary: {
main: PRIMARY_COLOR, // "#121212"
},
secondary: {
main: SECONDARY_COLOR, // "#F7D600"
},
},
});
const Wrapper = ({ children }) => {
return (
<ThemeProvider theme={theme}>{children}</ThemeProvider>
);
};
This works for things like buttons:
<Button
variant="contained"
color="secondary"
/>
In this case, the hex color #F7D600 gets applied.
But when I try to use the same color on my components using makeStyles, it doesn't seem to recognize it. It just uses the default by Material UI:
const useStyles = makeStyles((theme) => ({
someElement: {
backgroundColor: theme.palette.primary.main // <- not working. it uses the default purple color
}
});
I also tried useTheme but it's the same result:
const SomeComponent = () => {
const theme = useTheme();
return (
<Box style={{ backgroundColor: theme.palette.primary.main }}></Box>
);
}
Any ideas what I could be missing?
I can't see your import statement, but I use MuiThemeProvider. That could be your issue. Everything else looks right to me
import { MuiThemeProvider } from '#material-ui/core';
Use the below style to override any component in material-UI
import { createMuiTheme, colors } from '#material-ui/core'
const theme = createMuiTheme({
palette: {
background: {
dark: '#F4F6F8',
default: colors.common.white,
paper: colors.common.white,
},
primary: {
main: colors.indigo[500],
},
secondary: {
main: colors.indigo[500],
},
text: {
primary: colors.blueGrey[900],
secondary: colors.blueGrey[600],
},
common: {
tableHeader: '#DEF3FA',
},
action: {
oddRowColor: '#def3fa2e',
},
},
zIndex: {
modal: 10010, // override modal zIndex
appBar: 1000, // override Appbar
},
overrides: { 'MTableHeader-header': { root: { width: '143px !important' } } },
})
// use in your component
const StyledTableRow = withStyles((theme) => ({
root: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.action.oddRowColor,//defined in theme
},
padding: 'dense',
},
}))(TableRow)
[https://material-ui.com/customization/default-theme/][1]

Theme nesting with Material UI

I have many datatables throughout my website and for the most part they are all styled the same. There are several different styles I need to apply to some of them. I want to create a global theme for handling everything across the site including the basic datatable styles and I also want to have a local theme to tweak the individual datatables a little.
Here is what I've got.
https://codesandbox.io/embed/jolly-antonelli-fg1y1
This is structure like this
<Test>
<PrimaryThemeHere> //All have Border 1px red
<TestChild>
<SecondaryThemeHere> //blue background
<Datatable />
</SecondaryThemeHere>
</TestChild>
<TestChild2>
<SecondaryThemeHere> //Red background
<Datatable />
</SecondaryThemeHere>
<TestChild2>
</PrimaryThemeHere>
</Test>
The primary theme looks like this:
const theme = createMuiTheme({
overrides: {
MuiTableBody: {
root: {
border: "1px solid red"
}
},
MuiTableCell: {
root: {
border: "1px solid red"
}
}
}
});
and the nested theme looks like this:
getMuiTheme = () =>
createMuiTheme({
overrides: {
MuiTableRow: {
root: {
backgroundColor: "blue"
}
}
}
});
I can never get the border red to show alongside the background color. It always chooses one or the other. How can I get a combination of the initial primary theming (border 1px red) and the background color or blue and red.
Please help
Here's the relevant portion of the documentation:
https://material-ui.com/customization/themes/#nesting-the-theme
The code that handles theme nesting can be found here:
https://github.com/mui/material-ui/blob/master/packages/mui-system/src/ThemeProvider/ThemeProvider.js
Here is the current code:
// To support composition of theme.
function mergeOuterLocalTheme(outerTheme, localTheme) {
if (typeof localTheme === 'function') {
const mergedTheme = localTheme(outerTheme);
warning(
mergedTheme,
[
'Material-UI: you should return an object from your theme function, i.e.',
'<ThemeProvider theme={() => ({})} />',
].join('\n'),
);
return mergedTheme;
}
return { ...outerTheme, ...localTheme };
}
Notice that the final line (return { ...outerTheme, ...localTheme };) is doing a shallow merge of the two themes. Since both of your themes have the overrides property specified, the localTheme overrides will completely replace the outerTheme overrides.
However, you can do a more sophisticated merge of the two themes, by providing a function to the ThemeProvider. For instance TestChild can look like this:
import React, { Component } from "react";
import { MuiThemeProvider } from "#material-ui/core/styles";
import MUIDataTable from "mui-datatables";
const localTheme = {
overrides: {
MuiTableRow: {
root: {
backgroundColor: "blue"
}
}
}
};
const themeMerge = outerTheme => {
// Shallow copy of outerTheme
const newTheme = { ...outerTheme };
if (!newTheme.overrides) {
newTheme.overrides = localTheme.overrides;
} else {
// Merge the overrides. If you have the same overrides key
// in both (e.g. MuiTableRow), then this would need to be
// more sophisticated and you would probably want to use
// a deepMerge function from some other package to handle this step.
newTheme.overrides = { ...newTheme.overrides, ...localTheme.overrides };
}
return newTheme;
};
class TestChild extends Component {
render() {
const columns = [
{
name: "Message"
},
{
name: "Date"
},
{
name: "Dismiss"
}
];
const data = [["test", "15/01/19", "", ""], ["test", "15/01/19", "", ""]];
let options = {
filterType: "dropdown",
responsive: "stacked",
print: false,
search: false,
download: false,
selectableRows: "none"
};
return (
<div>
<MuiThemeProvider theme={themeMerge}>
<MUIDataTable
title={"Test"}
data={data}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
);
}
}
export default TestChild;
In my version of your sandbox, I only fixed TestChild2.js.
For me the whole inner theme worked, except the mode. I could fix it by adding a <Paper /> component.
import { createTheme, Paper, ThemeProvider } from "#mui/material";
const outerThemeOptions = {
palette: { mode: "light" },
typography: { body1: { fontSize: 14 } },
};
const innerThemeOptions = {
palette: { mode: "dark" },
};
const outerTheme = createTheme(outerThemeOptions);
const innerTheme = createTheme({
...outerThemeOptions,
...innerThemeOptions,
});
<ThemeProvider theme={outerTheme}>
<Child1 />
<ThemeProvider theme={innerTheme}>
<Paper elevation={0}>
<Child2 />
</Paper>
</ThemeProvider>
</ThemeProvider>;

createMaterialTopTabNavigator backgroundColor not defaulting to theme primary color

I am trying to use my theme primary color for the background of my createMaterialTopTabNavigator, but it is not defaulting to the theme color, and I can't access the theme outside of a component.
Basically I am using React-Native-Paper's default theme right now and it looks something like this (This purple color is the color I want):
Now the MaterialTopBarNavigator looks like this (It's not using the theme's default purple color):
So basically, I am trying to find a way to access my theme outside of a component so I can pass it into the style object in createMaterialTopTabNavigator tabBarOptions
My index.js looks like this
const myDarkTheme = {
...DarkTheme,
headerDark: true
};
const myDefaultTheme = {
...DefaultTheme,
headerDark: true
};
export const ThemeContext = React.createContext(null);
export default class Main extends Component {
state = {
theme: myDefaultTheme
};
_toggleTheme = () => {
this.setState(prevState => {
return {
theme: prevState.theme.dark ? myDefaultTheme : myDarkTheme
};
});
};
render() {
return (
<ThemeContext.Provider
value={{ theme: this.state.theme, toggleTheme: this._toggleTheme }}
>
<PaperProvider theme={this.state.theme}>
<App />
</PaperProvider>
</ThemeContext.Provider>
);
}
}
AppRegistry.registerComponent(appName, () => Main);
Here is my createMaterialTopTabNavigator
const AccountNavigator = createMaterialTopTabNavigator(
{
Profile: { screen: Profile },
Preferences: { screen: Preferences }
},
{
initialRouteName: "Profile",
swipeEnabled: true,
animationEnabled: true,
tabBarOptions: {
labelStyle: { fontWeight: "bold" },
//I would add this here to change background color
//but I don't know how to access my theme color from here...
//style: { backgroundColor: insertThemePrimaryColorHere }
}
}
);

How do you access the Material UI theme inside event handlers?

I have event handlers for things like onClick or onFocus, and I can't figure out how to use the theme inside of the handler code. I want to change the color of an iconButton and I don't want to hard-code the color because we want components that can be general use, and eventually work with themes using completely different colors.
Tried using withTheme in addition to withStyles, so I can get the theme inside of the render(), but I can't get to it from a handler called from that rendering. Tried passing it, calling as a prop, declaring constants based upon theme values in the class (both inside and outside of render), nothing.
I don't know if this is possible, or not built in, or what. I'm hoping that I'm just missing something.
Environment: CodeSandBox, so CreateReactApp. Material-UI plus React-Select, withStyles and withTheme (useTheme help here?).
handleInfoClick = (e) => {
if (this.instructionsContent.current.style.display !== "block") {
this.instructionsContent.current.style.display = "block";
this.instructionsButton.current.style.color = "#f9be00"; //works
} else {
this.instructionsContent.current.style.display = "none";
this.instructionsButton.current.style.color = this.theme.palette.text.disabled; // doesn't work
also tried this:
handleSelectFocus = () => {
if (this.state.visited === false) {
this.instructionsContent.current.style.display = "block";
this.instructionsButton.current.style.color = this.activeButtonColor;
this.setState({ visited: true });
}
};
...
render() {
const { theme } = this.props;
...
const activeButtonColor = theme.palette.secondary.main;
Finally, also tried to use the classes I can use within render(), but it doesn't recognize those either:
const styles = theme => ({
...
infoButton: {
position: "absolute",
bottom: 0,
left: 0,
marginBottom: 20,
width: 48,
color: theme.palette.text.disabled,
"&:active": {
color: theme.palette.secondary.main
}
},
infoButtonActive: {
position: "absolute",
bottom: 0,
left: 0,
marginBottom: 20,
width: 48,
color: theme.palette.secondary.main
},
....
Hoping one of these approaches would give me a color for my <IconButton> - from my theme:
<div className={classes.infoButtonDiv}>
<IconButton
aria-label="Instructions"
className={classes.infoButton}
buttonRef={this.instructionsButton}
onClick={this.handleInfoClick}
>
<HelpOutline />
</IconButton>
</div>
(in a different theme.js file applied to the root element:
const theme = createMuiTheme({
typography: {
fontFamily: ["Roboto", '"Helvetica Neue"', "Arial", "sans-serif"].join(",")
},
palette: {
primary: {
main: "#00665e"
},
secondary: {
main: "#f9be00"
}
},
overrides: {
LeftNav: {
drawerDiv: {
backgroundColor: "#00665e",
width: 300
}
}
},
direction: "ltr",
typography: {
useNextVariants: true
}
});
Triggering a state change onClick will update the color, but only if you pass one of the supported values for the IconButton color prop ("primary" or "secondary").
import React, { Component } from "react";
import IconButton from "#material-ui/core/IconButton";
import DeleteIcon from "#material-ui/icons/Delete";
class ButtonStyle extends Component {
constructor(props) {
super(props);
this.state = {
buttonColor: "primary"
};
}
handleClick = e => {
this.setState({
buttonColor: "secondary"
});
};
render() {
const buttonColor = this.state.buttonColor;
return (
<div>
<IconButton
aria-label="Delete"
color={buttonColor}
onClick={this.handleClick}
>
<DeleteIcon />
</IconButton>
</div>
);
}
}
export default ButtonStyle;

Resources