Cannot load modal with different data - reactjs

I am using material ui in react and after building the modals it loads only the last element of the object array.
<Grid container className={classes.root} spacing={2}>
<Grid item xs={12}>
<Grid container justify="center" spacing={spacing}>
{Object.keys(props.listOfAndis).map((keyName, i) => (
<Grid item>
<Card className={classes.card}>
<CardContent>
<Typography>Word of the Day {i}</Typography>
<Typography>be{bull}nev{bull}o{bull}lent</Typography>
<Typography>adjective</Typography>
<Typography>well meaning and kindly.<br/>{'"a benevolent smile"'}</Typography>
</CardContent>
<CardActions>
<div>
<Button size="small" onClick={handleOpen}>View Profile</Button>
</div>
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={open}
onClose={handleClose}>
<div style={modalStyle} className={classes.paper}>
<IconButton onClick={handleClose} tooltip="Close"
style={pointer && topRight}> X </IconButton>
<h2 id="simple-modal-title">Current
Project: {props.listOfAndis[keyName].currentProject}</h2>
<p id="simple-modal-description">Name: {props.listOfAndis[keyName].name}</p>
<p id="simple-modal-description">E-mail: {props.listOfAndis[keyName].emailAddress}</p>
<p id="simple-modal-description">Skills: {props.listOfAndis[keyName].skills}</p>
</div>
</Modal>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Grid>
</Grid>
Currently I have 3 mock entries, but only the last one is used for all three modals.
I have tried using key={i} with no success. What am I doing wrong?

Related

MUI breakpoints not working with an expandable drawer

So i have a drawer, and ive modified it with a default width of 450px, and a max width 0f 800px. I have xs set 6, and sm set to 4. But it doesnt change. It sets it to 4, and remains, no matter if i expand it to 800, or 450.
<Box>
<Grid container spacing={2} sx={{padding:'20px'}}>
{catalog.map((item, key) => {
return (
<Grid xs={6} sm={4} key={key}>
<Card className="media-catalog-item" sx={{height:'250px'}}>
<CardContent sx={{padding:'0px'}}>
<div className="media-item-title">{item.info.label}</div>
<div onClick={()=>{clickMedia(item)}}>
<img src={"https://example.com" + item.overlay}></img>
<img src={"https://example.com" + item.thumbnail}></img>
</div>
<div className= {list ? "list-item-footer" : "grid-item-footer"}>{item.info.displayName}</div>
</CardContent>
<CardActions>
<Button onClick={() => {notesOperations(item)}}>Notes</Button>
<Button onClick={() => {keywordsOperations(item)}}>Keywords</Button>
</CardActions>
</Card>
</Grid>
)
})}
</Grid>
</Box>
So it should start at 2 items on a row, and as i expand up to 600px+, switch to 3 on a row.
this is using MUI grid v2. Should I just use the regular grid and add item?
You missed to add item property to the Grid
<Box>
<Grid container spacing={2} sx={{padding:'20px'}}>
{catalog.map((item, key) => {
return (
// here you missed the item props
<Grid item xs={6} sm={4} key={key}>
<Card className="media-catalog-item" sx={{height:'250px'}}>
<CardContent sx={{padding:'0px'}}>
<div className="media-item-title">{item.info.label}</div>
<div onClick={()=>{clickMedia(item)}}>
<img src={"https://example.com" + item.overlay}></img>
<img src={"https://example.com" + item.thumbnail}></img>
</div>
<div className= {list ? "list-item-footer" : "grid-item-footer"}>{item.info.displayName}</div>
</CardContent>
<CardActions>
<Button onClick={() => {notesOperations(item)}}>Notes</Button>
<Button onClick={() => {keywordsOperations(item)}}>Keywords</Button>
</CardActions>
</Card>
</Grid>
)
})}
</Grid>
</Box>

Set grey background around main area

I am trying to get the look of this app using material ui:
Currently, my app looks like this:
There are a few things I need to do. Put some kind of box around the Keyword Processor text, put a box around the stuff below it, set background colour of central area boxes to white and have the background area of the rest to grey.
Any ideas how to modify my code below to do this?
<ThemeProvider theme={theme}>
</ThemeProvider>
<div>
<ThemeProvider theme={theme}>
<Container>
{/* <Box bgcolor="primary.main"> */}
<Grid container spacing={2}>
{/* <Grid xs={12}>
<AppBar />
<br />
</Grid> */}
<Grid item xs={12}>
<Typography variant="h6" gutterBottom>
Keyword Processor
</Typography>
{/* <br /> */}
</Grid>
<Grid item xs={6}></Grid>
<Grid item xs={6}>
<Box textAlign="right">
<Button
color="primary"
// mt={5}
onClick={this.inputToOutput}
//**********************
>
A-Z
</Button>
</Box>
{/* <Typography align="right">A-Z Frequency</Typography>{' '} */}
</Grid>
<Grid item xs={6}>
<TextField
label="Input Keywords"
fullWidth
multiline
rows={10}
variant="outlined"
margin="normal"
onChange={this.handleInputText}
defaultValue={defaultInputText}
/>
<br />
<Button
variant="contained"
color="primary"
mt={5}
onClick={this.inputToOutput}
//**********************
>
Parse
</Button>
<Button
variant="contained"
color="secondary"
mt={5}
// onClick={this.parseInput}
>
Clear
</Button>
<br />
<br />
<Checkboxes
handleDedupe={this.handleDedupe}
handleRemoveNumbers={this.handleRemoveNumbers}
handleConvertToLowercase={this.handleConvertToLowercase}
handleOneWordPerLine={this.handleOneWordPerLine}
handleAddCommas={this.handleAddCommas}
handleAddCommasSpace={this.handleAddCommasSpace}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Output Keywords"
fullWidth
multiline
rows={30}
variant="outlined"
margin="normal"
value={this.state.outputText}
/>
<Button
variant="contained"
color="primary"
mt={5}
onClick={this.clearOutput}
>
Copy
</Button>
<Button
variant="contained"
color="secondary"
mt={5}
onClick={this.clearOutput}
>
Clear
</Button>
</Grid>
<Grid>
<br />
<br />
</Grid>
</Grid>
{/* </Box> */}
</Container>
</ThemeProvider>
</div>
</>```
You don't need to do that all, here is what you can do instead. I believe the theme you added to ThemeProvider is custom, the edit the theme file pallete object to background colors i.e paper and default, the default color is what you need.
Here is mine:
import { createMuiTheme } from '#material-ui/core/styles';
// Main Material UI theme
const theme = createMuiTheme({
palette: {
background: {
paper: '#fff',
default: '#fafafa',
},
},
});
export default theme;
Change '#fafafa' to the color you want.
If you didn't create a custom theme, just copy mine then import it in your ThemeProvider, it will change the background of your body in all your html pages.

Space between two buttons in Material Design

I have two buttons and would like to put a space between them. So, I have tried to do that by putting a box around them and then applying a margin to the box. See code below.
render() {
return (
<>
<ThemeProvider theme={theme}>
<CssBaseline />
<BrowserRouter>
<Header />
</BrowserRouter>
</ThemeProvider>
<ThemeProvider theme={theme}>
<Grid container justify="center">
<Box pt={3}>
<Paper>
<Box pl={3} pr={3} pb={3} pt={3}>
<Grid container spacing={0}>
<Grid item xs={6}>
<Typography
variant="h6"
gutterBottom={false}
onClick={this.handleSetDummyText}
>
Keyword Processor
</Typography>
</Grid>
<Grid item xs={6}>
{/* ********* Offending code section ********** */}
<Button
color={
this.state.sorttypeStatus === 'default'
? 'primary'
: 'secondary'
}
onClick={this.handleSortDefault}
>
DEFAULT
</Button>
{/* ********* Offending code section ********** */}
<Button
color={
this.state.sorttypeStatus === 'alpha'
? 'primary'
: 'secondary'
}
onClick={this.handleSortAlpha}
>
A-Z
</Button>
<Button
color={
this.state.sorttypeStatus === 'length'
? 'primary'
: 'secondary'
}
onClick={this.handleSortLength}
>
LENGTH
</Button>
</Grid>
<Grid container spacing={2}>
<Grid item xs={6}>
<TextField
label="Input Keywords"
fullWidth
multiline
rows={10}
variant="outlined"
margin="normal"
onChange={this.handleInputText}
// defaultValue={defaultInputText}
value={this.state.inputText}
/>
<Box m={1}>
<Button
variant="contained"
color="primary"
onClick={this.inputToOutput}
>
Parse
</Button>
<Button
variant="outlined"
color="secondary"
onClick={this.clearInput}
// default={this.state.inputText}
>
Clear
</Button>
</Box>
<br />
<Checkboxes
handleDedupe={this.handleDedupe}
handleRemoveNumbers={this.handleRemoveNumbers}
handleConvertToLowercase={
this.handleConvertToLowercase
}
handleOneWordPerLine={this.handleOneWordPerLine}
handleAddCommas={this.handleAddCommas}
handleAddCommasSpace={this.handleAddCommasSpace}
handleStartWords={this.handleStartWords}
handleEndWords={this.handleEndWords}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Output Keywords"
fullWidth
multiline
rows={30}
variant="outlined"
margin="normal"
value={this.state.outputText}
/>
<Button
variant="contained"
color="primary"
mt={5}
onClick={() => {
navigator.clipboard.writeText(
this.state.outputText
);
}}
>
Copy
</Button>
<Button
variant="outlined"
color="secondary"
mt={5}
onClick={this.clearOutput}
>
Clear
</Button>
<Button
variant="outlined"
color="secondary"
mt={5}
onClick={this.handleSetDummyText}
>
Dummy Data
</Button>
</Grid>
</Grid>
</Grid>
</Box>
</Paper>
</Box>
</Grid>
</ThemeProvider>
</>
);
}
}
But the boxes seem to stick together like glue! I do have a custom theme but I cannot seem to get that to work with my App.js file because I am using a class component, rather than a functional component. I understand that you are supposed to use withStyles with class components, but I have struggled to get that to work. So, as a fix, I am trying the Box method.
Any idea how to get my box method to work?
you could use Grid component of material ui and as justify content use space-between
<Grid
style={{width:'140px'}}
container
direction="row"
justify="space-between"
alignItems="center">
<Button/>// here are the buttons
<Button/>
</Grid>
read more about the Grid https://material-ui.com/components/grid/
or you could do it with <div/> i used inline style so that you can read it
easier
<div style ={{ width:'200px',
display:'flex',
flexDirection:'row',
justifyContent:'space-between'
}}>
<button>button 1</button>
<button>button 2</button>
</div>
see working examples with div https://codesandbox.io/s/youthful-taussig-jyesc?file=/src/App.js:67-263
Have you tried to put each button in a box?
<div>
<Box m={1}>
<Button variant="contained">one</Button>
</Box>
<Box m={1}>
<Button variant="contained">two</Button>
</Box>
</div>
The <Box> component works like a <div>, it is a block, so your code can not align buttons in a row.
You can use flex-box to style to get what you want. Use Grid item, container and spacing API of Grid fit your need as well.
Below is an example:
<Grid container spacing={4}>
<Grid item xs={2}>
<Button fullWidth variant="contained" color="primary">
button A{" "}
</Button>
</Grid>
<Grid item xs={2}>
<Button fullWidth variant="contained" color="primary">
button B{" "}
</Button>
</Grid>
</Grid>
When using <Grid container>, you create a flex-box container. By default on ReactJS, the flex-items inside will be aligned by row. That's the reason why We do not have to declare props direction="row" on the container.
You can change the spacing value to change the space between two Grid items. By default, 1 = 8px and it can be customized in material ui as well.
xs indicates the size of your screen, in material ui we have 5 of them xs, sm, md, lg and xl, you can read about it in material ui docs.
fullWidth buttons just to make the buttons take the whole width of containers for ease of seeing space between buttons.
Working example on codesandbox: https://codesandbox.io/s/angry-wescoff-lnj71?file=/src/App.js

Show No data message if no records found in reactjs functional component

I am new to reactjs. I need to check whether if records are present, then I need to show grid using map function. Else I need to show message "No data". This is my code. I used ternary operator to do so
<Grid container spacing={2}>
{data.length > 0 ?
{data.map((activity) => (
<Grid
item
xs={6}
sm={4}
md={3}
lg={3}
onClick={() => {
setSpec(activity.spec)
setActivity(activity)
handleClickOpen(activity.spec)
}}
className={classes.thumbMain}
>
<ButtonBase focusRipple className={classes.fullwidthBtn}>
<Card className={classes.manage}>
<Box mt={2} mb={1}>
<Box
className={classes.mainIcons}
></Box>
</Box>
<Typography className={classes.cardlabel}>{(activity.name)}</Typography>
</Card>
</ButtonBase>
</Grid>
))}
: <p>No data</p>
}
</Grid>
The above code returns following error
Parsing error: ',' expected : in data.map((activity)
You shouldn't wrap data.map(...) in braces:
<Grid container spacing={2}>
{data.length ? (
data.map((activity) => (
<Grid
item
xs={6}
sm={4}
md={3}
lg={3}
onClick={() => {
setSpec(activity.spec);
setActivity(activity);
handleClickOpen(activity.spec);
}}
className={classes.thumbMain}
>
<ButtonBase focusRipple className={classes.fullwidthBtn}>
<Card className={classes.manage}>
<Box mt={2} mb={1}>
<Box className={classes.mainIcons}></Box>
</Box>
<Typography className={classes.cardlabel}>
{activity.name}
</Typography>
</Card>
</ButtonBase>
</Grid>
))
) : (
<p>No data</p>
)}
</Grid>;
You should wrap JS code in braces only one time to use it in JSX. If you add braces in other braces then you will be getting that error again.

How to dynamically create button for calling specific action using map array in react

I tried to map an array in react and tried to generate a button that will perform a specific action ,that is referencing another object generated by the same array using map() function.I'm using material-ui to speed up my development process.
I am very new to react (actually this is my first project with react), so maybe this is just simple question to implement 'state' in react, but i'm a little bit confusing to use this and bind syntax properly.
P.S -So excuse me for my stupidity :>
Follow this link to reproduce the code
and this is the code i got trouble with:
const products = [
{
id: 1,
img: "https://image.flaticon.com/icons/png/512/676/676434.png",
title: "Pineaple",
price: "Rp. 14.000",
desc: "Pineaple is one of nutritious food"
},
{
id: 2,
img: "https://image.flaticon.com/icons/png/512/676/676433.png",
title: "Banana",
price: "Rp. 14.000",
desc: "Banana is one of nutritious food"
},
{
id: 3,
img: "https://image.flaticon.com/icons/png/512/676/676441.png",
title: "Dragonfruit",
price: "Rp. 14.000",
desc: "Dragonfruit is one of nutritious food"
},
];
export default function Posts(props) {
const [open, setOpen] = React.useState(false);
function handleClickOpen() {
setOpen(true);
}
function handleClose() {
setOpen(false);
}
return (
<div>
<Grid container spacing={1} justify="center">
{products.map(product => (
<Grid item xs={6} sm={3} md={2} key={product.id}>
<Card>
<CardActionArea>
<CardMedia
component="img"
width="auto"
height="auto"
image={product.img}
/>
<CardContent>
<Typography component="h2"> {product.title} </Typography>
<Typography variant="body2" color="primary" component="p">
{" "}{product.price}{" "}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button onClick={handleClickOpen}>
Buy
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
{products.map(product => (
<Dialog
key={product.id}
fullScreen
open={open}
onClose={handleClose}
>
<AppBar position="sticky">
<Toolbar>
<IconButton onClick={handleClose}>
<CloseIcon />
</IconButton>
<Typography> {product.title} </Typography>
<Button onClick={handleClose}> buy </Button>
</Toolbar>
</AppBar>
<List className={classes.dialogue}>
<img src={product.img} alt={product.title} />
<ListItem button>
<ListItemText primary={product.title} secondary={product.desc}/>
</ListItem>
</List>
</Dialog>
))}
</div>
);
}
I want to make onclick button generated by mapped array to reference to specific action (show specific dialog within array list). I also want to implement same method for onSubmit on 'buy' button in the Dialog.
Screenshoot: https://imgur.com/a/M4v5LOu
(I click buy on 'pineaple' but react render all list and show the latest object in a the list which is 'dragonfruit'.)
I guess i'll use redux but maybe not right now.
Anyway that's it, I really appreciate any response and helps :)
Thanks!
There are several ways you can solve this but I will show you one. You are making use of React Hooks and you have a hook for setting the open/close state. In my solution, I make slight modification by adding another hook to set the selected product and then checking if both open and the product are set.
export default function Posts(props) {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const [product, setProduct] = React.useState(null);
function handleClickOpen(event, item) {
event.persist();
setProduct(item);
setOpen(true);
}
function handleClose() {
setOpen(false);
}
return (
<div style={{ margin: 0, padding: 0 }}>
<Grid container spacing={1} justify="center">
{products.map(product => (
<Grid item xs={6} sm={3} md={2} key={product.id}>
<Card elevation={0}>
<CardActionArea>
<CardMedia
component="img"
width="auto"
height="auto"
image={product.img}
/>
<CardContent>
<Typography component="h2"> {product.title} </Typography>
<Typography variant="body2" color="primary" component="p">
{' '}
{product.price}{' '}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button
variant={'outlined'}
size="small"
color="primary"
onClick={event => handleClickOpen(event, product)}
>
Buy
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
{open && product && (
<Dialog
key={product.id}
className={classes.dialogue}
fullScreen
open={open}
onClose={handleClose}
BackdropProps={{ classes: { root: classes.root } }}
PaperProps={{ classes: { root: classes.paper } }}
>
<AppBar position="sticky">
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={handleClose}
aria-label="Close"
>
<CloseIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
{product.title}
</Typography>
<Button color="inherit" onClick={handleClose}>
buy
</Button>
</Toolbar>
</AppBar>
<List className={classes.dialogue}>
<Image
className={classes.images}
src={product.img}
alt={product.title}
/>
<ListItem button>
<ListItemText primary={product.title} secondary={product.desc} />
</ListItem>
</List>
</Dialog>
)}
</div>
);
}
In your code, you didn't have a way to track the currently selected product hence you always get the last item in the loop. By using another hook for the selected product, I can track the selected product. I hope this helps you and good luck in your mastery of React.
You are having two states open and close.
You are using map on array and showing the dialog box.
The dialog box will open when open state is true.
This will be true for all elements in the array. Dialog box will be shown for all elements.
Now, they will overlap on each other and you can only see the last one.
When you click on close dialog your open state set to false and all the dialogs are closed.
Hint :-
Maintain a state that will contain the id of element for which dialog is to be shown. Show dialog only when id state matches with the element's id

Resources