Using MUI V5, how can I pass a custom style to a button component? Here is what I have been trying to merge the old way with the new MUI v5 but it's not working.
import { Button } from "#mui/material";
import { styled } from "#mui/material/styles";
import React from "react";
const StyledButton = styled(Button)(({ theme }) => ({
root: {
minWidth: 0,
margin: theme.spacing(0.5),
},
secondary: {
backgroundColor: theme.palette.secondary.light,
"& .MuiButton-label": {
color: theme.palette.secondary.main,
},
},
primary: {
backgroundColor: theme.palette.primary.light,
"& .MuiButton-label": {
color: theme.palette.primary.main,
},
},
}));
export default function ActionButton(props) {
const { color, children, onClick } = props;
return (
<Button
className={`${StyledButton["root"]} ${StyledButton[color]}`}
onClick={onClick}
>
{children}
</Button>
);
}
Now I would like to call this Button and give it color="secondary"
import ActionButton from "./ActionButton";
import { Close } from "#mui/icons-material";
export default function Header() {
return (
<ActionButton color="secondary">
<Close />
</ActionButton>
)
}
It looks like your code was an attempt to migrate from code using makeStyles/useStyles, but styled works quite a bit differently. You can't use it to generate multiple CSS classes like makeStyles does (StyledButton["root"] and StyledButton[color] will be undefined). styled will generate a single CSS class that is then passed in the className prop to the wrapped component (e.g. Button). Instead of trying to create multiple CSS classes with logic to decide which class to apply, with styled you can leverage props (e.g. the color prop) to generate dynamic styles within the single CSS class that is generated.
Below is an example that I think achieves the effect your code was aiming for. My example doesn't do anything with MuiButton-label because that class doesn't exist in v5 (and the <span> that the class was applied to inside the <button in v4 also does not exist), and I believe the default Button styles set color as desired when the color prop is allowed to passed through to Button.
import Button from "#mui/material/Button";
import { styled } from "#mui/material/styles";
const StyledButton = styled(Button)(({ theme, color }) => ({
minWidth: 0,
margin: theme.spacing(0.5),
backgroundColor: color ? theme.palette[color].light : undefined
}));
export default StyledButton;
Related
I'm creating some custom components for my application and they essentially have some base styling done to them for light/dark modes. My goal is to be able to use those components with all their props later when the custom component is being used to stay flexible. How do I achieve this?
For example if I style a custom input component and use it, I want to be able to tap into the secureTextEntry prop when needed. Here is an example of what I have right now for my CustomText. I want to be able to style this further when needed.
import { Text, useColorScheme } from 'react-native';
import React from 'react';
type CustomTextProps = {
text: string;
};
const CustomText = ({ text }: CustomTextProps) => {
const isDarkMode = useColorScheme() === 'dark';
return <Text style={{ color: isDarkMode ? '#fff' : '#000' }}>{text}</Text>;
};
export default CustomText;
react-native expose interfaces for each component.
so you need to extend your interface with TextProps:
import { Text, TextProps } from 'react-native';
interface CustomTextProps extends TextProps {
text: string;
};
By extending those interfaces (e.g. TextProps) in CustomTextProps we can have all text element props passed to this component.
Instead of having to declare each one we can just use a spread attribute ...rest
const CustomText = ({ text, ...rest }: CustomTextProps) => {
const isDarkMode = useColorScheme() === 'dark';
return <Text style={{ color: isDarkMode ? '#fff' : '#000' }} {...rest}>{text}</Text>;
};
export default CustomText;
This sounds like a job for React Context which acts as a store for global state that you can access using useContext hook
Background
So I want to style my component and all its children with a text highlight color to be yellow for example. But I am only able to do it if the component returns the text wrapped within the element itself, and won't work with any text wrapped in its child elements or child components.
For instance, this will change the highlight color:
import React from "react";
import { Container, styled } from "#mui/material";
const StyledContainer = styled(Container)(({ theme }) => ({
"::selection": {
backgroundColor: "#ffe20b"
},
}));
function App() {
return <StyledContainer>hello world</StyledContainer>;
}
export default App;
But if I wrap the text in a child element div the styling won't work.
import React from "react";
import { Container, styled } from "#mui/material";
const StyledContainer = styled(Container)(({ theme }) => ({
"::selection": {
backgroundColor: "#ffe20b"
},
}));
function App() {
return <StyledContainer><div>hello world</div></StyledContainer>;
}
export default App;
Question
How would I be able to recursively set the style throughout my App? Let's say my most-outter element is a <Box></Box> and it has a class named MuiBox-root that we can use.
I have tried the following but none worked:
// didn't work
const StyledContainer = styled(Container)(({ theme }) => ({
margin: theme.spacing(0),
".MuiBox-root::selection": {
backgroundColor: "#ffe20b"
},
// the following didn't work either
const StyledContainer = styled(Container)(({ theme }) => ({
margin: theme.spacing(0),
".MuiBox-root > * ::selection": {
backgroundColor: "#ffe20b"
},
If you highlight the displayed two lines of text, you can see that the first line is styled while the second line isn't. Here is the codesandbox
link
You can try following it will work for all child elements
const StyledBox = styled(Box)(({ theme }) => ({
"::selection, *::selection": {
backgroundColor: "#ffe20b"
}
}));
can somebody explain how pass in the defauklt theme in Material UI5
in Material UI6 i use to do it like this
const useStyles = makeStyles((theme) => ({
home: {
display: "flex",
paddingTop: "8rem",
width: "100vw",
height: "100vh",
backgroundColor: theme.palette.primary.dark,
color: "white",
},
}));
but as i got throught M-UI5 docs (as far as i found) there is no explanation on how it can be done , the only part they mention about makeStyle it contains this code in this page docs
+import { makeStyles } from '#mui/styles';
+import { createTheme, ThemeProvider } from '#mui/material/styles';
+const theme = createTheme();
const useStyles = makeStyles((theme) => ({
background: theme.palette.primary.main,
}));
function Component() {
const classes = useStyles();
return <div className={classes.root} />
}
// In the root of your app
function App(props) {
- return <Component />;
+ return <ThemeProvider theme={theme}><Component {...props} /></ThemeProvider>;
}
so am i suppose to run createTheme() on every component to get the theme? , apology if i missed out an obvious thing in the docs , probably coz my poor english
The part you are missing is from this part of the migration guide: https://mui.com/material-ui/guides/migration-v4/#style-library.
if you are using JSS style overrides for your components (for example overrides created by makeStyles), you will need to take care of the CSS injection order. To do so, you need to have the StyledEngineProvider with the injectFirst option at the top of your component tree.
Without this, the default styles for the MUI Card in your About component win over the styles specified via classes.about where the styles overlap (e.g. background).
Changing your AllProviders component to the following (just adding <StyledEngineProvider injectFirst>) fixes it:
import React from "react";
import CountriesProvider from "./countries-context";
import QuestionsProvider from "./questions-context";
import {
ThemeProvider,
createTheme,
StyledEngineProvider
} from "#mui/material/styles";
const theme = createTheme();
const AllProviders = (props) => {
return (
<StyledEngineProvider injectFirst>
<QuestionsProvider>
<ThemeProvider theme={theme}>
<CountriesProvider>{props.children}</CountriesProvider>
</ThemeProvider>
</QuestionsProvider>
</StyledEngineProvider>
);
};
export default AllProviders;
https://codesandbox.io/s/funny-flower-w9dzen?file=/src/store/AllProviders.js:303-342
The theme was being passed in fine without this change (otherwise you would have had errors when it tried to access parts of the theme), but the CSS injection order was not correct.
This is react-native question but similar concepts can be applied to react.
I want to create a CustomView in react-native. I am using typescript.
So far, I have:
const styles = StyleSheet.create({
container: {
backgroundColor: '#ffffff',
borderRadius: 10,
}
});
type CustomViewProps= {
width: number,
height: number,
marginTop?: string | number,
}
const CustomView = ({ width, height, marginTop }: CustomViewProps) => (
<View style={[styles.container, { height, width, marginTop }]} />
);
This is ok so far because only 3 props are being used: width, height and marginTop.
However, this is not reusable and it can become verbose if I need to add many more props.
So, the question is: How can I make CustomView receive any props as a native component View could receive?
My guess is I should delete CustomViewProps. Then, I should make the props inherit from the same type that the native component View does. However, I am struggling with it.
Since you are creating CustomViewProps, I assume that you want to add some specific behaviours to your native component above the already written behaviours of that component.
Let's create an example.
I want to create a button with some specific behaviours but i want it to behave, in other cases, like a normal TouchableOpacity component. For example, i want to add a "loading" state which will show a loader inside instead of its content.
So the logic is: create your custom props and merge you custom props with native's default props
import React, { FC, ReactElement } from 'react'
import { ActivityIndicator, TouchableOpacity, TouchableOpacityProps, StyleSheet } from 'react-native'
type MyProps = {
loading?: boolean
children: ReactElement
}
const MyButton: FC<MyProps & TouchableOpacityProps> = (props: MyProps & TouchableOpacityProps) => (
<TouchableOpacity {...props} disabled={props.disabled || props.loading} style={[styles.button, props.style]}>
{props.loading ? <ActivityIndicator /> : props.children}
</TouchableOpacity>
)
const styles = StyleSheet.create({
button: {
backgroundColor: 'yellow',
borderColor: 'black',
borderWidth: 1,
borderRadius: 10,
padding: 10
},
})
export default MyButton
The loading prop will be responsible for both content of the button or is disabled prop. The TouchableOpacity component will receive every compatible prop (autosuggest will be enabled because you have assigned the TouchableOpacityProps). The styles.button will behave like default style but will be overwritten if you specify something different in your style prop. That's it!
I have a simple React JS component that wraps around the really cool react ChartistGraph component. The only issue is that the styling is seemingly overridden by the ChartistGraph default CSS. There is a lot of info on the regular Chartist js package but not much on the React JS package.
As you can see, I'm trying to change the fill color two ways: through style classes and through a prop that supported on the component.
import React from 'react';
import { Paper, withStyles } from 'material-ui';
import ChartistGraph from 'react-chartist';
const styles = theme => ({
graphStyle: {
fill: 'red',
},
});
const CustomChart = ({ classes, graph }) => {
return (
<Paper>
<ChartistGraph
className={classes.graphStyle}
data={graph.data}
options={graph.options}
type={graph.type}
style={{ fill: 'red' }}
/>
</Paper>
);
};
export default withStyles(styles)(CustomChart);
A picture of the styles of the chart
You can use jss's nested rules (included by default in material-ui):
const styles = theme => ({
graphStyle: {
'& .ct-label': { fill: 'red' },
},
});
Full code:
import React from 'react';
import { Paper, withStyles } from 'material-ui';
import ChartistGraph from 'react-chartist';
const styles = theme => ({
graphStyle: {
'& .ct-label': { fill: 'red' },
},
});
const CustomChart = ({ classes, graph }) => {
return (
<Paper>
<ChartistGraph
className={classes.graphStyle}
data={graph.data}
options={graph.options}
type={graph.type}
// style={{ fill: 'red' }} // omitted
/>
</Paper>
);
};
export default withStyles(styles)(CustomChart);
I got into similar issue recently.React-Chartist is built on top of react not material-ui.When you inspect,you found regular css class names,not "material ui-ish" class-names(like MuiTable-root,MuiTable-selectedRow,etc).So ,imho,it won't support material-ui methods (withStyle/makeStyle) and rules.
But what you can do is:-
create a css file and put your styles there
And import it where you want
.You can import it on the main file of your app(index.js or whatever it is) since every css in your app will bundle in one file.