How do you set the material-ui cardholder avatar? - reactjs

I'm trying to use the material-ui CardHolder component -- https://material-ui.com/api/card-header/ . I have this image in my project
src/img/apple.svg
so I tried to set the avatar image like so
<Card className={classes.root} {...rest}>
<CardHeader
avatar="img/apple.svg"
title={title}
titleTypographyProps={{ variant: "h2", component: "span" }}
className={classes.cardHeader}
/>
<CardContent className={classes.cardContent}>
but instead what is printed out is just the path of my image. What's the proper way to output the avatar as a real image?

You can make use of Avatar component from Material-ui and pass it on as a avatar prop.
the problem in your code is that you are trying to pass on a src to it whereas it takes in a Node
<Card className={classes.root} {...rest}>
<CardHeader
avatar={<Avatar alt="Apple" src="img/apple.svg" />}
title={title}
titleTypographyProps={{ variant: "h2", component: "span" }}
className={classes.cardHeader}
/>
<CardContent className={classes.cardContent}>

The avatar prop accepts a node, so you can use avatar api like this one
avatar={<Avatar alt="An apple" src="img/apple.svg" />}
So your code will become
<Card className={classes.root} {...rest}>
<CardHeader
avatar={<Avatar alt="An apple" src="img/apple.svg" />}
title={title}
titleTypographyProps={{ variant: "h2", component: "span" }}
className={classes.cardHeader}
/>
<CardContent className={classes.cardContent}>

Are you sure avatar takes an image? From what I can see in the demo. In the component.
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
R
</Avatar>
}
In the styles:
avatar: {
backgroundColor: red[500],
},
Can't find an image attribute for avatar props.

Related

Progress bar component not showing up within Group/Stack

Whenever I try to place a progress bar inside a Group/Stack, it does not show up on my webpage. It works only when I place it outside the Group/Stack. Here is the code and results:
import { Stack, ThemeIcon, Text, Progress, Group, Button } from "#mantine/core";
interface SkillProps {
name: string;
icon: JSX.Element;
}
function Skill(props: SkillProps) {
return (
<>
<Group>
<ThemeIcon size={50} radius={25} variant="outline">
{props.icon}
</ThemeIcon>
<Stack align="center" spacing={5}>
<Text>{props.name}</Text>
</Stack>
<Progress value={50} />
</Group>
</>
);
}
export default Skill;
When placed within Group/Stack: Result
When placed outside Group/Stack: Result
Anyone has any idea why this is happening?
It seems that this might be because Progress does not have a specified width from parent container when in Group or Stack.
For use in Group, try add flex:"1" to Progress with the sx prop, which will make it take the remaining space in Group:
<Group>
<ThemeIcon size={50} radius={25} variant="outline">
{props.icon}
</ThemeIcon>
<Stack align="center" spacing={5}>
<Text>{props.name}</Text>
</Stack>
<Progress value={20} sx={{ flex: "1" }} />
</Group>
For use in Stack, try add alignSelf: "stretch" instead for similar result:
<Group>
<ThemeIcon size={50} radius={25} variant="outline">
{props.icon}
</ThemeIcon>
<Stack align="center" spacing={5}>
<Text>{props.name}</Text>
<Progress value={20} sx={{ alignSelf: "stretch" }} />
</Stack>
</Group>

Different color in a custom component React

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

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>}

Material-UI useAutocomplete cannot access option in onChange

I don't know what I am doing wrong but I cannot access the option.slug from the object I receive from the API. I am trying to change pass a URL slug and change the route of my app. I am trying to do this using the onChange prop inside my useAutocomplete() definition.
Accessing the option.slug or any other property works well inside of my JSX structure. As you can see I am grabbing option.title and so on to render my list items which works well...
Instead of grabbing the actual slug I keep getting the route of "/components/undefined"
But when I try accessing the option.slug on the top level of my component in the useAutocomplete() definition it doesn't seem to work. Based on the original documentation though, that's the way to do it.
https://material-ui.com/components/autocomplete/#useautocomplete
export default function NavigationSearch({ onClose, results }) {
const router = useRouter();
const {
getRootProps,
getInputLabelProps,
getInputProps,
getListboxProps,
getOptionProps,
groupedOptions
} = useAutocomplete({
id: 'use-autocomplete-demo',
options: results,
getOptionLabel: (option) => option.title,
onChange: (option) => router.push(`/components/${option.slug}`)
});
return (
<React.Fragment>
{/* 1. SEARCH INPUT */}
<Container maxWidth="md" disableGutters>
<Box display="flex" width="100%">
<motion.div
style={{ width: 'inherit' }}
initial="hidden"
animate="visible"
variants={animation}>
<Box display="flex" width="inherit">
<Search width="inherit" {...getRootProps()}>
<SearchIcon color="primary" />
<InputBase
placeholder="Search for components, patterns..."
style={{ color: 'inherit', width: 'inherit' }}
autoFocus
{...getInputProps()}
/>
</Search>
</Box>
</motion.div>
<IconButton color="primary" onClick={onClose}>
<CloseIcon />
</IconButton>
</Box>
</Container>
{/* SEARCH RESULTS */}
<BackdropOverlay open={true} onClick={onClose}>
<Container maxWidth="md" disableGutters>
{groupedOptions.length > 0 ? (
<Results>
<List
{...getListboxProps()}
subheader={
<Box paddingX={2}>
<Typography
variant="overline"
color="textSecondary"
gutterBottom>
Popular search results
</Typography>
</Box>
}>
{groupedOptions.map((option, index) => (
<Item
button
key={option.title}
{...getOptionProps({
option,
index
})}>
<Box
display="flex"
justifyContent="space-between"
width="100%">
<Box>
<Typography color="textPrimary">
{option.title}
</Typography>
<Typography variant="caption" color="textSecondary">
{option.type}
</Typography>
</Box>
<Box alignSelf="flex-end">
<Typography variant="caption" color="textSecondary">
/components/button
</Typography>
</Box>
</Box>
</Item>
))}
</List>
</Results>
) : null}
</Container>
</BackdropOverlay>
</React.Fragment>
);
}
This is the API response that I store in the results prop:
0: {id: 1, title: "Button", slug: "button"}
1: {id: 2, title: "Switch", slug: "switch"}
2: {id: 3, title: "Tags", slug: "tags"}
3: {id: 4, title: "Checkbox", slug: "checkbox"}
4: {id: 5, title: "Toast", slug: "toast"}
Autocomplete component uses useAutocomplete hook under the hood so they share the same API. In Autocomplete API. This is the signature of onChange:
function(event: object, value: T | T[], reason: string) => void
The second argument is the option value which is what you need here. So change your code to:
onChange: (_, option) => router.push(`/components/${option.slug}`)
To fix the undefined value issue.

MUI: how to position AppBar on left

Using the React Material UI library (material-ui.com), how do you position the AppBar to the left side of the screen? The design I need to build has a the global nav (which you'd typically see across the top) along the left, with fixed position. There are also links in the nav that will slide panels out from the left (from "behind" the global nav).
I can't find anything in the documentation which supports this design. Is it possible?
Thanks
I was able to accomplish this using an AppBar in a flex container using flexDirection="row".
<React.Fragment>
<CssBaseline />
<Box display="flex" flexDirection="row" className={classes.root}>
<Box flexGrow={0}>
<AppBar elevation={0} position="sticky">
<div>Left</div>
<div>appBar</div>
<Button>try me</Button>
<Divider />
<Button>try me2</Button>
</AppBar>
</Box>
<Box display="flex" flexDirection="row" flexGrow={1}>
<Box
display="flex"
flexDirection="column"
className={classes.mainArea}
flexGrow={1}
></Box>
</Box>
<Box flexGrow={0}>
<AppBar elevation={0} position="sticky">
<div>Right</div>
<div>appBar</div>
<Button>try me</Button>
<Divider />
<Button>try me2</Button>
</AppBar>
</Box>
</Box>
</React.Fragment>
Code SandBox Demo

Resources