Instead of using multiple props for each css attribute, I want to make an sx prop like material-ui has. I'm trying to implement this with styled-components. The idea is that I can pass an object as a prop, and reference a value from the object in the styled component.
things to note:
material-ui is not added as a dependancy. I want to "clone" sx from it.
currently looking into prop-types to add some value in finding bug.
Button Component
import styled from "styled-components";
export const Button = styled.button`
background-color: ${(sx) => sx.backgroundColor};
color: ${(sx) => sx.color};
`;
Rendering Button
import React from "react";
import styled from "styled-components";
import { Button } from "./Button/Button";
function App() {
return (
<>
<Button sx={{ backgroundColor: "red", color: "white" }} />
<>
);
}
export default App;
Update:
After adding prop-types to maybe find the issue, I get an error when I pass the prop to the Button component when I added this code:
// typechecking prop types passed
Button.propTypes = {
sx: PropTypes.string,
};
And the error I got back:
Warning: Failed prop type: Invalid prop `sx` of type `object` supplied to `styled.button`, expected `string`
Im not sure exactly where its going wrong. I've tried doing a console.log to see the value passed, which seems to be a string. Therefore dot notation should work?
Any help would be appreciated, thanks very much :)
After adding prop-types and finding the passed types, I was able to find this answer:
// required prop to be passed as an object
Button.propTypes = {
sx: PropTypes.object,
};
// updating the props for Button component
export const Button = styled.button`
background-color: ${(props) => props.sx.backgroundColor};
color: ${(props) => props.sx.color};
`;
then using sx as normal...
import React from "react";
import styled from "styled-components";
import { Button } from "./Button/Button";
function App() {
return (
<>
<Button sx={{ backgroundColor: "red", color: "white" }} />
</>
);
}
export default App;
Related
I am trying to override the Mui styles by using the classes prop.
For example, I would like to override the InputLabel color of the TextField component.
I would like to use one definition of makeStyles that will set all css rules, starting at the root (TextField) and overriding whatever I want in the hierarchy (for example, the InputLabel when focused), when passing it to the component in the classes prop.
How is it done?
import * as React from "react";
import TextField from "#mui/material/TextField";
import makeStyles from "#mui/styles/makeStyles";
const useStyles = makeStyles({
root: {
color: "yellow",
label: {
color: "brown"
}
}
});
export default function Input() {
const classes = useStyles();
return (
<TextField
classes={classes}
id="outlined-basic"
label="Outlined"
variant="outlined"
/>
);
}
Answer
import * as React from "react";
import TextField from "#mui/material/TextField";
import makeStyles from "#mui/styles/makeStyles";
const useStyles = makeStyles({
root: {
"& .MuiInputBase-input": {
color: 'red',
padding: "0.2rem"
},
},
});
export default function Input() {
const classes = useStyles();
return (
<TextField
InputProps={{ classes }}
/>;
);
}
codesandbox
The classes prop in MUI gives you access to the CSS Rule Names for a component. When you're using this prop, check the API spec for that component. The CSS Rule Names are at the bottom of the page.
https://mui.com/api/text-field/#css
For this component, there is only 1 rule available (root), so this will (I think) have the same effect as just using className.
What you probably want to do is use the InputProps prop instead. The Input component has way more CSS Rules you can target: https://mui.com/api/input/#css
So, I think you probably want to do this:
<TextField
InputProps={{ classes }}
id="outlined-basic"
label="Outlined"
variant="outlined"
/>
EDIT to help answer comment:
I don't think you need to target InputBase, as I believe you can target input instead. To target the input CSS Rule, just change the root key to input:
import * as React from "react";
import TextField from "#mui/material/TextField";
import makeStyles from "#mui/styles/makeStyles";
const useStyles = makeStyles({
input: {
color: 'red',
padding: "0.2rem"
},
});
export default function Input() {
const classes = useStyles();
return (
<TextField
InputProps={{ classes }}
/>;
);
}
The CSS rules in the docs are sensitive to the keys in the object you pass.
The best way I found is to use the Mui v5 sx property.
(makeStyles is deprecated - https://mui.com/guides/migration-v4/#2-use-tss-react)
(input signifies the rule in https://mui.com/api/input-base/#css)
import * as React from "react";
import TextField from "#mui/material/TextField";
export default function Input() {
const classes = useStyles();
return (
<TextField
sx={{
"& input":
{ padding: "0rem",color: "blue" }
}}
/>
);
}
Is there a way to pass the return of another variable to the styled constructor "styled(xx)"?
I get an error: Objects are not valid as a React child (found: object with keys {$$typeof, render, propTypes}).
import styled from 'styled-components/macro';
import { Link as LinkR } from 'react-router-dom';
import { Link as LinkS } from 'react-scroll';
const Link = props => (props.scroll ? LinkS : LinkR);
export const BtnContainer = styled(Link)`
margin: 16px 0;
padding: 16px 22px;
`
If I wrap the imported Link objects into `` or '' in the Link variable, it only outputs the return to the DOM. Ok, I expected that. :) But it shows me that something can be passed on to the constructor.
Wrapping in {} or $ {} do not work either.
Have you tried returning the rendered link components?
const Link = (props) => props.scroll ? <LinkS {...props} /> : <LinkR {...props} />;
This makes Link an actual component and styled-components can handle them
I'm trying to use Container component with styled-components using ContainerProps but then I can't pass component prop which belongs to OverridableComponent interface.
Code below gives me error which tells me that I can't pass component property. When I change <Container/> to <MuiContainer/> it works.
MuiContainer has type OverridableComponent<ContainerTypeMap<{}, 'div'>> but I can't import OverridableComponent from #material-ui/core
How can I make passing component property possible?
import { Container as MuiContainer, ContainerProps } from '#material-ui/core';
import React from 'react';
import styled from 'styled-components';
const Container = styled(MuiContainer)<ContainerProps>``;
export const Test = () => {
return (
<>
<Container maxWidth="lg" component="main">
content
</Container>
</>
);
};
Usage of component prop
const Container = styled(MuiContainer)<ContainerProps<'main', { component: 'main' }>>``;
You can tell the Typescript compiler to retain the original Container prop types by asserting it:
import MuiContainer from "#mui/material/Container";
import { styled } from "#mui/material/styles";
const Container = styled(MuiContainer)`
background-color: pink;
padding: 16px;
` as typeof MuiContainer;
export default function StyledComponents() {
return (
<Container maxWidth="lg" component="main">
content
</Container>
);
}
Live Demo
Reference
https://mui.com/guides/composition/#with-typescript
I understand the basic of react code such as passing data to child component and passing data back to parent with props. I encountered this code below from React Material UI which is abit confusing. Was hoping to get some explanation.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import CircularProgress from '#material-ui/core/CircularProgress';
import purple from '#material-ui/core/colors/purple';
const styles = theme => ({
progress: {
margin: theme.spacing.unit * 2,
},
});
function CircularIndeterminate(props) {
const { classes } = props;
return (
<div>
<CircularProgress className={classes.progress} />
<CircularProgress className={classes.progress} size={50} />
<CircularProgress className={classes.progress} color="secondary" />
<CircularProgress className={classes.progress} style={{ color: purple[500] }} thickness={7} />
</div>
);
}
CircularIndeterminate.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(CircularIndeterminate);
Here is what I understand.
1.The component takes in props so I can pass data down to this child component and before render it will extract the classes property from props.classes.
2.There is a styles function that takes in a theme variable and returns an object progress which has some styling which is used by classes.progress
3.withStyle is some sort of higher order component.
But with this knowledge, this code is abit confusing.
How come the code can do classes.progress, why does classes variable have anything to do with the progress style
What is the purpose of
CircularIndeterminate.propTypes = {
classes: PropTypes.object.isRequired,
};
classes variable that is that is passed in as a prop is an object with progress being one of its key. The classes object might look like
classes = {
progress: 'half'
}
Now the value of classes.progress which in the above classes object is half is assigned as a className prop to the CircularProgress component.
Regarding
CircularIndeterminate.propTypes = {
classes: PropTypes.object.isRequired,
};
The above syntax is to add typechecking capabilities to your component. React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special propTypes property.
In your case, it means that the CircularIndeterminate expects a prop classes which is of type object and is a Required prop.
To read more about PropTypes, please visit the docs
I'd like to add Flow Types to an existing React project which uses Material UI.
I have a number of components which render a Material UI component and pass through any props received. For example:
import React from "react";
import IconButton from "#material-ui/core/IconButton";
import backIcon from "assets/icons/Back.svg";
const BackButton = ({ height, ...props }) => (
<IconButton {...props}>
<img
src={backIcon}
alt="back"
style={{
height,
width: height
}}
/>
</IconButton>
);
BackButton.defaultProps = {
height: "1.5rem"
};
export default BackButton;
If I were to define prop types for this, I figure it would look something like
type BackButtonProps = IconButtonProps & {
height: string
}
const BackButton = ({height, ...props}): BackButtonProps => (
...
)
My issue is that I'm not sure where to get IconButtonProps from. I've run flow-typed install and have a flow-typed/npm/#material-ui folder, but I can't figure out how to import IconButtonProps. I've tried the following:
import type { IconButtonProps } from "#material-ui/core/IconButton"
But the import is unresolved.
Where can I import the prop types of Material UI components from, in order to be able to extend them?