I need to return either Porover or Card depending on the condition with the same children content. This can be done by taking the children content into a separate component and returning it like this:
true ? <Popover><ChildrenContent props={props}/></Popover>
: <Card><ChildrenContent props = {props}/></Card>
But it seems to me that this is not the best solution in this case
<Popover dataCy="complexValidation">
<Section marginBottom={3}>
</Section>
<Flex flexDirection="column" gap={3}>
{validations.map((validation) => (
<Flex.Item key={validation.text}>
<Flex alignItems="center">
<Icon name="check_circle" size="large" color={validation.isSuccess ? 'positive' : 'negative'} />
<Flex.Item>
<Typography variant="bodyText2" component="span">
{validation.text}
</Typography>
</Flex.Item>
</Flex>
</Flex.Item>
))}
</Flex>
</Popover>
I am using AntD Collapse for displaying a list of items after expand icon is clicked.
I want the position of expandIcon to go to bottom-right after all the list of the data when expand icon is clicked (just like in google news), but found only two options (left|right) for 'expandIconPosition', no option for top or bottom.
How can we align the expandIcon to bottom-right, when expand icon is clicked?
Few lines from the code for reference:
<Collapse
ghost
style={{ marginTop: "-1vh" }}
expandIcon={({ isActive }) => (
<DownOutlined
style={{ marginTop: "-2vh" }}
rotate={isActive ? 180 : 0}
/>
)}
expandIconPosition="right"
>
<Panel>
<div>
{list()} //list of items
</div>
</Panel>
</Collapse>
Here's one possible solution. Make Collapse a controlled component by specifying activeKey prop and then the value of it will be based on state. Then, by tracking the activeKeys state you can now do a conditional rendering (hide and show) on icons:
const [activePanelKeys, setActivePanelKeys] = useState([]);
const handlePanelIconClick = (panelKey, makeActive) => {
if (makeActive) {
setActivePanelKeys([...activePanelKeys, panelKey]);
} else {
setActivePanelKeys(activePanelKeys.filter((aPK) => aPK !== panelKey));
}
};
<Collapse
activeKey={activePanelKeys}
expandIconPosition="right"
expandIcon={() => <DownOutlined />}
// expandIcon={(panelProps) => (
// <DownOutlined
// onClick={() => handlePanelIconClick(panelProps.panelKey, true)}
// />
// )}
onChange={e => setActivePanelKeys(e)} //if you want to click only icon, comment this and uncomment the above expandedIcon
>
<Panel
header="This is panel header 1"
key="p1"
showArrow={activePanelKeys.indexOf("p1") === -1}
>
<div>{text}</div>
{activePanelKeys.indexOf("p1") !== -1 && (
<div style={{ textAlign: "right", marginTop: 10 }}>
<UpOutlined onClick={() => handlePanelIconClick("p1", false)} />
</div>
)}
</Panel>
{/* <PanelContent header="This is panel header 2" key="p2">
{text}
</PanelContent> */}
</Collapse>;
Here is the complete sample code:
Note: I tried to make a reusable Panel component but it seems that the reveal animation were gone. You can uncomment the commented PanelContent on the code to see the difference.
Hope that I hit what you want.
i am trying to create a Dynamic Material UI Flex box that generate a new row based on backend . my idea is to close the current outer Box and create a new one based on a flag
so i write the following code
<Box p="1em">
<Box display="flex">
{tabs.map((t, index) => {
return (
<>
<Box flex={resourcesTabs[index][0] == null ? 1 : resourcesTabs[index][0]['width_class']} ml="1em">
<Typography variant="h6" gutterBottom>{t}</Typography>
{resourcesFields[index] && resourcesFields[index].map((f, index) =>
generateInputField(f, index)
)}
</Box>
{resourcesTabs[index][0]['new_line_after'] && </Box><Box display="flex">}
</>
);
})}
</Box>
</Box>
But i recive the following Error
Parsing error: Unexpected token
as it complain about close open tags dynamic for this line
{resourcesTabs[index][0]['new_line_after'] && (</Box><Box display="flex">}
any idea how to solve this ?
There is a sneaky parenthesis:
{resourcesTabs[index][0]['new_line_after'] && => ( <= </Box><Box display="flex">}
i fixed it by replace box with grid as the following code
<div style={{ padding: 10 }}>
<Grid container spacing={3}>
{tabs.map((t, index) => {
return (
<>
<Grid item xs={resourcesTabs[index][0] == null ? 6 : resourcesTabs[index][0]['width_class']} >
<Typography variant="h6" gutterBottom>{t}</Typography>
{resourcesFields[index] && resourcesFields[index].map((f, index) =>
generateInputField(f, index)
)}
</Grid>
</>
);
}
)}
</Grid>
</div>
I've added some piece of code in render method of react using conditional (ternary) operator. The thing is the condition is rendered true also the control goes into the block. But when I print the value the variable is undefined
I tried inserting an alert inside the block to check if the control traverses the block and it worked.
const comments =
details.comments_count >= 1
? Object.keys(details.comments_list).forEach(keys => {
return (
<ListItem
alignItems="flex-start"
key={details.comments_list[keys].aid}
>
<ListItemAvatar>
<Avatar>{details.comments_list[keys].aname.charAt(0)}</Avatar>
</ListItemAvatar>
<Card style={useStyles.card}>
<CardContent>
<ListItemText
primary={details.comments_list[keys].aname}
secondary={details.comments_list[keys].content}
/>
</CardContent>
</Card>
</ListItem>
);
})
: null;
console.log(comments);
//=>undefined
I don't actually know why this this is happening, given the result of conditional expression is true. Any help is appreciated. Thanks.
forEach won't return anything but undefined. You need to use map.
const comments =
details.comments_count >= 1
? Object.keys(details.comments_list).map(keys => {
return (
<ListItem
alignItems="flex-start"
key={details.comments_list[keys].aid}
>
<ListItemAvatar>
<Avatar>{details.comments_list[keys].aname.charAt(0)}</Avatar>
</ListItemAvatar>
<Card style={useStyles.card}>
<CardContent>
<ListItemText
primary={details.comments_list[keys].aname}
secondary={details.comments_list[keys].content}
/>
</CardContent>
</Card>
</ListItem>
);
})
: null;
console.log(comments);
It's looks like CardMedia need an image while component is created. Since I am pulling the image data via componentDidMount (RestAPI) then the component is already mount.
componentDidMount() {
// get all items via category ID and owner ID
const restApi = new API({ url: 'api' })
restApi.createEntity({ name: 'items' })
// api/items/<categoryId>/<ownerId>
restApi.endpoints.items.getTwo({ id_a: this.props.categoryId, id_b: this.props.ownerId }).then(({ data }) => this.setState({ appData: data }))
}
render() {
const { classes } = this.props;
let classNameHolder = [classes.redAvatar, classes.greenAvatar, classes.blueAvatar, classes.purpleAvatar];
this.state.appData.map(element => {
this.state.images.push(element.imageUrl);
});
return (
<Card>
<CardHeader
avatar={
<Avatar aria-label="Recipe"
className={classNameHolder[Math.floor(Math.random() * classNameHolder.length)]}>
{this.props.userName.charAt(0).toLocaleUpperCase()}
</Avatar>}
title={this.props.userName} disableTypography={true} />
<CardActionArea disabled={this.state.images.length === 1 ? true : false}>
<CardMedia
id={this.props.ownerId}
className={classes.media}
image={this.state.images[this.state.imageIndex]}
onClick={this.handleOnClick} />
</CardActionArea>
</Card >
);
}
}
I can move the all API one level up so I use the props in order to pass data image but I would like to know if you guys have any some elegant solution.
or you can do a simple check in the component itself so to avoid resetting the state, display a mui spinner for instance while content loads
this will fix the warning and display nice feedback to the user
<>
{imgUrl ? (
<CardMedia
component="img"
alt="Contemplative Reptile"
height="140"
src={imgUrl}
title="Contemplative Reptile"
/>
) : (
<Spinner />
)}
</>
I had also faced same problem,
I simply added a image link in image prop of <CardMedia>.
Like,
<CardMedia
id={this.props.ownerId}
className={classes.media}
image={this.state.images[this.state.imageIndex] || 'https://user-images.githubusercontent.com/194400/49531010-48dad180-f8b1-11e8-8d89-1e61320e1d82.png'}
onClick={this.handleOnClick} />
For me it solved, by just adding a image link.
You can use the Skeleton component to display a rectangle placeholder with the same height as the image while it is being fetched:
{loading ? (
<Skeleton sx={{ height: 140 }} animation="wave" variant="rectangular" />
) : (
<CardMedia component="img" height="140" image={YOUR_IMAGE_URL} />
)}
Not sure what appData contains, but I made some changes to your code, hopefully is gonna give you a better understanding.
render() {
const { classes } = this.props;
let classNameHolder = [classes.redAvatar, classes.greenAvatar, classes.blueAvatar, classes.purpleAvatar];
/*
// you don't save the return of the next array anywhere, so this map is doing nothing.
this.state.appData.map(element => {
// you cannot redefine state like this, to redefine state you have to use setState
this.state.images.push(element.imageUrl);
});
*/
const imagesUrl = this.state.appData.map(el => el.imageUrl);
return (
< Card >
<CardHeader
avatar={
<Avatar aria-label="Recipe"
className={classNameHolder[Math.floor(Math.random() * classNameHolder.length)]}>
{this.props.userName.charAt(0).toLocaleUpperCase()}
</Avatar>}
title={this.props.userName} disableTypography={true} />
{/* This part I don't undestand much what you try to do, I can propose use map of imagesUrl. But to help you more I need to know more info about what you try to do here */}
{/* Let's not render this part of the component instead of disabled it */}
{imagesUrl.length > 0 &&
<CardActionArea disabled={this.state.images.length === 1 ? true : false}>
<CardMedia
id={this.props.ownerId}
className={classes.media}
image={this.state.images[this.state.imageIndex]}
onClick={this.handleOnClick} />
</CardActionArea>
}
</Card >
);
}
}
Suggestion reviewing your array appData, maybe is good idea print the content after you retrieve it, let's see an example.
const { classes } = this.props;
const classNameHolder = [classes.redAvatar, classes.greenAvatar, classes.blueAvatar, classes.purpleAvatar];
const AvatarComponent = (
<Avatar aria-label="Recipe"
className={classNameHolder[Math.floor(Math.random() *
classNameHolder.length)]}>
{this.props.userName.charAt(0).toLocaleUpperCase()}
</Avatar>
);
return (<div className="Cards">
{this.state.appData.map(data => (
<Card>
<CardHeader avatar={AvatarComponent} title={this.props.userName} disableTypography={true} />
<CardActionArea disabled={data.imageUrl !== ''}>
<CardMedia
id={this.props.ownerId}
className={classes.media}
image={this.state.images[this.state.imageIndex]}
onClick={this.handleOnClick} />
</CardActionArea>
</Card>
))}
</div>);
Like this, we wait to get the async data before rendering the component, previously I changed, instead of disabling the component just prevent render it if you still don't have the images.
I faced with the same problem and solved it by initializing that state in the constructor. The "CardMedia" has 'image' attribute which needs to receive a String, but when you want to send a state there, something like image={this.state.images[this.state.imageIndex]}, it returns null in the first rending time. You can solve the problem by adding the below code in your constructor.
this.state = {
images: 'some default address/link/values'
};
const photos = [
"http://i.photo.cc/300?img=1",
"http://i.photo.cc/300?img=2",
"http://i.photo.cc/300?img=3",
"http://i.photo.cc/300?img=4"
];
{photos.map(photo => (
<CardMedia image={photo} />
))}
Providing image prop with the direct URL also works!
just add component="image" and your are good to go
<CardMedia component="image"/>
To get the image in the CardMedia component follow the below way:-
Import the image from any folder to the desired componnent & it will be done !
import picture from "../assets/picture.jpg"
<CardMedia
component="img"
height="20%"
image={image}
alt="scene"
/>