Grid item not shrinking to fit the contained Scrollable List - reactjs

I am trying to get the grid item containing the scrollable list to shrink so the back button is just below the list.
Following is the code, and a link to CodeSandbox. There is a lot of grid and box components surrounding the list. These are extracts from other parent components so I get it all in one file for simplicity.
https://codesandbox.io/s/y2c9sn?file=/src/App.js
import * as React from "react";
import {
Box,
Button,
Grid,
List,
ListItem,
ListItemText,
ListItemButton,
AppBar,
Toolbar,
Typography,
} from "#mui/material";
const usersList = [
"user1",
"user2",
"user3",
"user4",
"user5",
"user6",
"user7",
"user8",
"user9",
"user10",
];
function App() {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
height: "100vh",
}}
>
<Grid container justifyContent="center">
<Grid item xs={12} md={8}>
<AppBar position="static">
<Toolbar>
<Typography variant="h6">Scrollable List</Typography>
</Toolbar>
</AppBar>
</Grid>
<Grid item xs={12} md={8}>
<Box>
<Grid container justifyContent="center">
<Grid item xs={12}>
<List sx={{ maxHeight: "50%", overflow: "auto" }}>
{usersList.map((user, index) => {
return (
<ListItem disablePadding key={index}>
<ListItemButton>
<ListItemText>{user}</ListItemText>
</ListItemButton>
</ListItem>
);
})}
</List>
</Grid>
</Grid>
<Grid container justifyContent="center">
<Grid item xs={8}>
<Button
name="back"
variant="contained"
color="primary"
value="back"
fullWidth
size="small"
>
Back
</Button>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
</Box>
);
}
export default App;
Link to CodeSandbox
https://y2c9sn.csb.app/

You can move this style:
sx={{ maxHeight: "50%", overflow: "auto" }}
from your List component to its first parent Grid container.
This sandbox is the updated version of your sandbox with this change.

Related

Having grid items display based on their size MUI Quilted Grid Layout

Image of Issue
Apologies for the zoomed out image, but I am using MUI in React to display a bunch of nested cards dynamically (The data has nested components rendering other components).
Each grid has cards that has grids within them and cards within the grids. I am trying to have the cards fit the page.
So in the image below, I would like the grid component on the second row and second column to come up to where the grid component on the first row second column ends.
I've been searching around trying to find the answer this question but a newbie to frontend so I am not sure how to phrase it. Any advice would be helpful!
Edit:
I am curious about making a quilted grid layout
const useStyles = makeStyles({
gridContainer: {
paddingLeft: "85px",
paddingRight: "85px",
},
root: {
minWidth: 200,
},
bullet: {
display: "inline-block",
margin: "0 2px",
transform: "scale(0.8)",
},
title: {
fontSize: 14,
},
pos: {
marginBottom: 12,
},
parentFlexRight: {
display: "flex",
justifyContent: "flex-end",
},
leftAlignItem: {
marginRight: "auto",
marginTop: "auto",
},
stretch: { height: "100%" },
item: {
display: "flex",
flexDirection: "column",
},
});
var checkIndex = 0;
const renderCourseRequirements = (RequiredCourses) => {
return (
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justifyContent="center"
>
{RequiredCourses.map((course, index) => {
var Index = checkIndex;
checkIndex = checkIndex + 1;
return (
<Grid key={index} item xs={12} sm={6} md={4}>
<Card sx={{ width: 200 }} variant="outlined">
<CardContent>
<Typography color="textSecondary" gutterBottom>
{course}
</Typography>
<Checkbox
id={`custom-checkbox-${Index}`}
name={course}
value={course}
checked={checkedState.includes(course)}
onChange={() => handleOnChange(course)}
/>
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
);
};
const renderComponents = (components) => {
return (
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justifyContent="center"
>
{components.map((component, index) => {
return (
<Grid
container
spacing={0}
direction="column"
alignItems="stretch"
justifyContent="center"
key={index}
item
xs={12}
sm={6}
md={4}
>
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
{component.component_name}
</Typography>
<Typography className={classes.pos} color="textSecondary">
Required Number of Courses: {component.required_num_courses}
</Typography>
{renderCourseRequirements(component.course_list)}
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
);
};
const renderComponentFamilies = (componentFamilies) => {
return (
<Grid container spacing={4} className={classes.item}>
{componentFamilies.map((componentFamily, index) => {
if (componentFamily.component_list.length > 1)
return (
<Grid key={index} xs={8} sm={6} md={4}>
<Card className={classes.stretch} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
{componentFamily.component_family_name}
</Typography>
<Typography className={classes.pos} color="textSecondary">
Required Number of Components :{" "}
{componentFamily.required_num_components}
</Typography>
{renderComponents(componentFamily.component_list)}
</CardContent>
</Card>
</Grid>
);
return (
<>
<Grid key={index} item xs={12} sm={6} md={4}>
{renderComponents(componentFamily.component_list)}
</Grid>
</>
);
})}
</Grid>
);
};
A couple of resources that may point you in the right direction. Setting some CSS breakpoints may also work with your content.
https://mui.com/system/sizing/
https://mui.com/material-ui/customization/default-theme/?expand-path=$.breakpoints.values
If that's not what you're looking for, I'd recommend wrapping your elements in an element that references a method with a 'resize' event listener that makes your preferred changes after the resize is triggered.

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.

How to limit the mui grid to a box

I made a multi-card grid layout with Mui grid
I want to limit the grid box to a particular region on screen, something like:
the user should have to scroll to see the rest
This is the code that doesn't work as I want it to
return (
<Box className={classes.pageContainer}>
<Typography className={classes.backgroundText}>WORKS</Typography>
<Box sx={{ height: '600px' }}>
<Grid container spacing={6} className={classes.gridContainer} justifyContent="center">
{temporaryVideos.map((cardInfo, ndx) => {
return (
<Grid item>
<VideoCard cardInfo={cardInfo} index={ndx} />
</Grid>
);
})}
</Grid>
</Box>
</Box>
);
Thanks in advance
Rafael
I did it
I placed an "overflowY: 'auto'" on the grid container

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>

How to put different content for each cards

I am using material ui album template for my react app
I want to put different image and different text for each cards for this when I remove this array it create the problems in responsiveness of the cards
I tried to put separte grid for each cards but thats some how solve the issue but that responsiveness of the template does not remain same
here is my demo code
https://codesandbox.io/s/material-demo-pz8df
Make sure you're updating the right cards array. Use an array of objects as well, where each object has a key for an image-link and one for the description. In the .map() we'll use these values to render the content. Here's a working sandbox: https://codesandbox.io/s/material-demo-3v44c
The responsiveness will work like expected.
Working code:
import React from "react";
import AppBar from "#material-ui/core/AppBar";
import Button from "#material-ui/core/Button";
import CameraIcon from "#material-ui/icons/PhotoCamera";
import Card from "#material-ui/core/Card";
import CardActions from "#material-ui/core/CardActions";
import CardContent from "#material-ui/core/CardContent";
import CardMedia from "#material-ui/core/CardMedia";
import CssBaseline from "#material-ui/core/CssBaseline";
import Grid from "#material-ui/core/Grid";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import { makeStyles } from "#material-ui/core/styles";
import Container from "#material-ui/core/Container";
import Link from "#material-ui/core/Link";
function MadeWithLove() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{"Built with love by the "}
<Link color="inherit" href="https://material-ui.com/">
Material-UI
</Link>
{" team."}
</Typography>
);
}
const useStyles = makeStyles(theme => ({
icon: {
marginRight: theme.spacing(2)
},
heroContent: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(8, 0, 6)
},
heroButtons: {
marginTop: theme.spacing(4)
},
cardGrid: {
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8)
},
card: {
height: "100%",
display: "flex",
flexDirection: "column"
},
cardMedia: {
paddingTop: "56.25%" // 16:9
},
cardContent: {
flexGrow: 1
},
footer: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(6)
}
}));
const cards = [
{
img:
"https://images.unsplash.com/photo-1564135624576-c5c88640f235?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3300&q=80",
desc: "Campsite"
},
{
img:
"https://images.unsplash.com/photo-1564198879220-63f2734f7cec?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2072&q=80",
desc: "Space"
}
];
export default function Album() {
const classes = useStyles();
return (
<React.Fragment>
<CssBaseline />
<AppBar position="relative">
<Toolbar>
<CameraIcon className={classes.icon} />
<Typography variant="h6" color="inherit" noWrap>
Album layout
</Typography>
</Toolbar>
</AppBar>
<main>
{/* Hero unit */}
<div className={classes.heroContent}>
<Container maxWidth="sm">
<Typography
component="h1"
variant="h2"
align="center"
color="textPrimary"
gutterBottom
>
Album layout
</Typography>
<Typography
variant="h5"
align="center"
color="textSecondary"
paragraph
>
Something short and leading about the collection below—its
contents, the creator, etc. Make it short and sweet, but not too
short so folks don&apos;t simply skip over it entirely.
</Typography>
<div className={classes.heroButtons}>
<Grid container spacing={2} justify="center">
<Grid item>
<Button variant="contained" color="primary">
Main call to action
</Button>
</Grid>
<Grid item>
<Button variant="outlined" color="primary">
Secondary action
</Button>
</Grid>
</Grid>
</div>
</Container>
</div>
<Container className={classes.cardGrid} maxWidth="md">
{/* End hero unit */}
<Grid container spacing={4}>
{cards.map(card => (
<Grid item key={card} xs={12} sm={6} md={4}>
<Card className={classes.card}>
<CardMedia
className={classes.cardMedia}
image={card.img}
title="Image title"
/>
<CardContent className={classes.cardContent}>
<Typography gutterBottom variant="h5" component="h2">
Heading
</Typography>
<Typography>{card.desc}</Typography>
</CardContent>
<CardActions>
<Button size="small" color="primary">
View
</Button>
<Button size="small" color="primary">
Edit
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
</main>
{/* Footer */}
<footer className={classes.footer}>
<Typography variant="h6" align="center" gutterBottom>
Footer
</Typography>
<Typography
variant="subtitle1"
align="center"
color="textSecondary"
component="p"
>
Something here to give the footer a purpose!
</Typography>
<MadeWithLove />
</footer>
{/* End footer */}
</React.Fragment>
);
}

Resources