Different color in a custom component React - reactjs

I created a custom component called InfoCard, I passed trough props the information, the component is working properly, the only issue I have is that I want to define a diferente color for the Icon prop element when I call it into other components
For ex: I need to be red to be green
Is there any a possibility to do it without using CSS classes ?
InfoCard Component
interface Props {
icon: JSX.Element
title: string
store?: string
price: string
divider?: JSX.Element
}
const Item = styled(Paper)(({ theme }) => ({
padding: theme.spacing(1),
margin: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
}))
const InfoCard = ({ icon, title, store, price, divider }: Props) => {
return (
<>
<Item>
<Stack direction="row" spacing={2} sx={{ justifyContent: 'center' }}>
<Box>{icon}</Box>
<Typography>{title}</Typography>
<Box>{divider}</Box>
<Typography>{store}</Typography>
</Stack>
<Typography variant="h5" sx={{ color: 'primary.main' }}>
{price}
</Typography>
</Item>
</>
)
}
export default InfoCard
Header Component
const Header = () => {
const { t } = useTranslation()
return (
<Box>
<Grid container>
<Grid item xs={12} sm={12} md={6} lg={4}>
<InfoCard
icon={<ArrowDownIcon className="smallIcon" />}
title={t('LOWEST_REGULAR_PRICE')}
store="store 1"
price="1.900"
divider={<Divider orientation="vertical" />}
/>
</Grid>
<Grid item xs={12} sm={12} md={6} lg={4}>
<InfoCard
icon={<ArrowTrendingUpIcon className="smallIcon" />}
title={t('AVERAGE_REGULAR_PRICE')}
price="1.900"
/>
</Grid>
<Grid item xs={12} sm={12} md={12} lg={4}>
<InfoCard
icon={<ArrowPathIcon className="smallIcon" />}
title={t('LAST_UPDATE')}
price="19/07/2022"
/>
</Grid>
</Grid>
</Box>
)
}
```

You can style a react element with the style prop as follows:
<MyComponent style={{color: "red"}} />
https://reactjs.org/docs/faq-styling.html
Hint: careful that passing rendered react components as props like you do in your code example, is usually not desired. icon={<ArrowTrendingUpIcon className="smallIcon" />} You may want to consider using props.children instead. https://reactjs.org/docs/react-api.html#reactchildren

Related

How can I automatically import an icon which fetches from the server in NextJS?

I want to dynamically generate a page in the Next-JS app. Inside this page should be imported automatically Icons which fetches from the server Instead of writing it statically:
{
"id": 1,
"title": "Budget",
"value": "$24K",
"persent" :"12%",
"duration" :"Since Last Month",
"icon":"MoneyIcon",
"rise":"false"
},
in this timeMoneyIcon from Material-UI.
In this case, was used map method in order to render fetched data.
How can I put this fetched icon name as a tag same as <MoneyIcon/> in the component?
{posts.map((post) => (
<>
<Grid item lg={3} sm={6} xl={3} xs={12}>
<Card sx={{ height: "100%" }}>
<CardContent>
<Grid container spacing={3} sx={{ justifyContent: "space-between" }}>
<Grid item>
<Typography color="textSecondary" gutterBottom variant="overline">
{post.title}
</Typography>
<Typography color="textPrimary" variant="h4">
{post.value}
</Typography>
</Grid>
<Grid item>
<Avatar
sx={{
backgroundColor: "error.main",
height: 56,
width: 56,
}}
>
**<MoneyIcon />**
</Avatar>
</Grid>
</Grid>
<Box
sx={{
pt: 2,
display: "flex",
alignItems: "center",
}}
>
<ArrowDownwardIcon color="error" />
<Typography
color="error"
sx={{
mr: 1,
}}
variant="body2"
>
{post.persent}
</Typography>
<Typography color="textSecondary" variant="caption">
{post.duration}
</Typography>
</Box>
</CardContent>
</Card>
</Grid>
</>
))}
If I understand you correctly you are trying to pass the MUI Icon to the Component and render the icon. For that you can simply pass the Icon as a value of your object wrapped in a React Fragment.
import AccountBalanceIcon from '#mui/icons-material/AccountBalance';
const myObject = {
id: 1,
value: "24K",
icon: <><AccountBalanceIcon/></>
)};
export default function MyComponent(props) {
return(
<div>
<span>{myObject.value}</span>
{myObject.icon}
<div>
)
}
You can import them dynamically by name for example you can fetch the Icon name from server then in the page do this:
import * as MuiIcons from '#mui/icons-material'
function YourPage({IconName}){
const IconComponent = MuiIcons[IconName]
return <Grid container>
<IconComponent />
</Grid>
}
and if you want for example #mui/icons-material/Memory the Icon name is "Memory".
But I think this approach is not treeshakable you may bring the entire jungle , I am not sure about that though, you should check the bundle after compiling.

Fetch API with Axios in React

const key = '618479acc2ef5ba8018516ac'
function UserPost1 () {
const [post, setPost] = useState(['']);
const handleExpandClick = () => {
setExpanded(!expanded);
};
useEffect(() =>{
axios.get('https://dummyapi.io/data/v1/post' , { headers: { 'app-id': key } })
.then(res => {
setPost(res.data.data)
console.log(res)
})
.catch(err =>{
console.log(err)
})
},[]);
return(
<div className="Post-style">
{post.map(post =>(
<Box sx={{ flexGrow: 1 }}>
<Grid container rowSpacing={0} columnSpacing={{ xs: 1, sm: 2, md: 2, lg: 2 }} >
<Grid
container
direction="row"
justifyContent="center">
<Grid item xs={4} sm={12} md={6} lg={4}>
<div className="card-Style">
<Card sx={{ width: 355}} style={{backgroundColor: "aquamarine"}} >
<CardHeader
avatar={
<Avatar
src={post.owner.picture}
/>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={post.owner.firstName + " " + post.owner.lastName}
subheader={post.publishDate}
/>
<CardMedia
component="img"
height="194"
image={post.image}
alt="Paella dish"
backgroundcolor="blue"
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
{post.text}
<br></br>
{post.likes}
<br></br>
{post.tags}
</Typography>
</CardContent>
</Card>
</div>
</Grid>
</Grid>
</Grid>
</Box>
))}
</div>
)
}
export default UserPost1;
When im run this code i cant get the data from API using Axios, it says error cannot read properties of undefined (reading 'picture'). I tried to catch the error but it does not show in console log. How do i solve this problem.
should i make the axios to wait until it gets the data API or make it to false.
What should i do, its my first time with API.
Somehow, the picture property is missing inside the owner object.
Add optional chaining before picture:
<CardHeader
avatar={
<Avatar
src={post.owner?.picture}
/>
}
...
Render your post component after complete async query and set state value. Use condition rendering and change default state value from [''] to null.
If your component has a specific height and width, then create a placeholder with the same values.
To exclude layout jumps during query execution, show a placeholder. Show the placeholder while the request is running, and after the request is complete and the value is set to the state, show the post component. Good luck!
// change default useState value
const [post, setPost] = useState(null);
// add condition rendering
{post ? post.map(post => (
<Box sx={{ flexGrow: 1 }}>
<Grid container rowSpacing={0} columnSpacing={{ xs: 1, sm: 2, md: 2, lg: 2 }} >
<Grid
container
direction="row"
justifyContent="center">
<Grid item xs={4} sm={12} md={6} lg={4}>
<div className="card-Style">
<Card sx={{ width: 355 }} style={{ backgroundColor: "aquamarine" }} >
<CardHeader
avatar={
<Avatar
src={post.owner.picture}
/>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={post.owner.firstName + " " + post.owner.lastName}
subheader={post.publishDate}
/>
<CardMedia
component="img"
height="194"
image={post.image}
alt="Paella dish"
backgroundcolor="blue"
/>
<CardContent>
<Typography variant="body2" color="text.secondary">
{post.text}
<br></br>
{post.likes}
<br></br>
{post.tags}
</Typography>
</CardContent>
</Card>
</div>
</Grid>
</Grid>
</Grid>
</Box>
)) : <div> PLACEHOLDER </div>}

vertical divider not working in material ui

I have this component that contains a card and inside this card there are elements and I want to separate them through a vertical line and the problem is that the vertical line does not work.
const useStyles = makeStyles((theme: Theme) =>
createStyles({
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
}
}),
);
const SpaceForm: FC = (props) => {
const classes = useStyles()
const workspaceData = useWorkspaceModule((state) => state.workspace)
console.log("inside component 1: ", workspaceData);
return (
<>
<Grid
container
spacing={3}
>
<Grid
item
lg={8}
md={6}
xs={12}
>
<Card>
<CardHeader title="Name your Workspace:"/>
<CardContent>
<Avatar style={{width: '3.4rem', height: '3.4rem'}} className={classes.orange}>N</Avatar>
{/*llll*/}
<Divider style={{ backgroundColor:'red'}} orientation="vertical" flexItem />
</CardContent>
</Card>
</Grid>
</Grid>
</>
);
};
export default SpaceForm;
You just wrap Avatar inside a flex Box and it will show Divider after Avatar:
<Box display="flex">
<Avatar
style={{ width: "3.4rem", height: "3.4rem" }}
className={classes.orange}
>
N
</Avatar>
{/*llll*/}
<Divider
style={{ backgroundColor: "red" }}
orientation="vertical"
flexItem
/>
</Box>

Material ui conditional rendering styles

I have 2 grids and I want one of them to be a black background and one to be a white background, both of these backgrounds are set in a theme in the app.js. Im wanting to use theme.palette.common.black or theme.palette.common.white if i pass in a white tag prop inside the second grid. Im trying to use a ternary statement but not sure how to implement it.
const styles = (theme) => ({
root: {
background: theme => theme.palette.common ? "black" : "white",
// background: theme.palette.common.black,
// background: theme.palette.common.white,
},
});
const HomePage = ({ classes }) => (
<>
<FullScreenBanners>
<Grid
className={classes.root}
container
spacing={0}
alignItems="center"
justify="center"
direction="column"
>
<Typography>iPhone SE</Typography>
<Typography>From £10.99/mo. or £279 with trade-in.</Typography>
<LearnMoreBuy />
<img src="./images/iphone-se.jpg" />
</Grid>
<Grid
white
className={classes.root}
container
spacing={0}
alignItems="center"
justify="center"
direction="column"
>
<Typography>iPhone 7</Typography>
<Typography>From £7/mo. or £200 with trade-in.</Typography>
<LearnMoreBuy />
<img src="./images/iphone-se.jpg" />
</Grid>
</FullScreenBanners>
</>
);
I hope I haven't misunderstood your question.
Wouldn't it be easier to just have two different classes - a standard one (root) and one for the white background (e.g. white)? Then, instead of passing in a 'white' prop to one of your Grid elements, you can just apply the second class name.
const useStyles = makeStyles({
root: {
background: theme.palette.common.black,
},
white: {
background: theme.palette.common.white,
},
});
const HomePage = () => {
const classes = useStyles();
return (
<>
<FullScreenBanners>
<Grid
//give this one just the root class
className={classes.root}
container
spacing={0}
alignItems="center"
justify="center"
direction="column"
>
<Typography>iPhone SE</Typography>
<Typography>From £10.99/mo. or £279 with trade-in.</Typography>
<LearnMoreBuy />
<img src="./images/iphone-se.jpg" />
</Grid>
<Grid
//give this one the root class and the white class
className={`${classes.root} ${classes.white}`}
container
spacing={0}
alignItems="center"
justify="center"
direction="column"
>
<Typography>iPhone 7</Typography>
<Typography>From £7/mo. or £200 with trade-in.</Typography>
<LearnMoreBuy />
<img src="./images/iphone-se.jpg" />
</Grid>
</FullScreenBanners>
</>
);
};

How to use theme in styles for custom functional components

When I try compiling the app it displays a certain error "TypeError:undefined has no properties" though I want to render styled components into the grid tags though in function based component method
const styles = theme => ({
root: {
height: '100vh',
},
image: {
},
paper: {
margin: theme.spacing(8, 4),
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%',
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
});
const SignIn = (props) => {
const classes = this.props;
return(
<Grid container component="main" className={classes.root}>
<CssBaseline />
<Grid item xs={false} sm={4} md={7} className={classes.image} />
<Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square>
<div className={classes.paper}>
<Typography component="h1" variant="h5">
Welcome to web
</Typography>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<form className={classes.form} noValidate>
//content
</form>
</div>
</Grid>
</Grid>
);
}
export default withStyles(styles)(SignIn);
Access or destructure classes from props, not this.props.
const { classes } = props;
You can also destructure props right in the function definition.
const SignIn = ({ classes }) => {...

Resources