MUI dropdown react application - material-ui

I am using MUI to develop my dashboard.
I am using MUI dropdown component to select the vendor details and there are two options for the Vendor dropdown. i.e "Surya Kumar Yadav", "Eswar". I used the same dropdown in all my cards. When I select the one of the dropdown in any card, it is reflected in all the other cards.
import React from "react";
import {
Box,
Card,
CardContent,
Typography,
Button,
CardActionArea,
CardActions,
Grid,
Select,
MenuItem,
FormControl,
InputLabel,
} from "#mui/material";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { HttpClient } from "../http/http";
import { ConfirmDialog, confirmDialog } from "./ConfirmDialog";
const Services = () => {
const [services, setServices] = useState([]);
const [vendors, setVednors] = useState([]);
const [selectedVendor, setSelectedVendor] = useState("");
const PF =
"https://firebasestorage.googleapis.com/v0/b/mern-stack-service-app.appspot.com/o/";
const navigate = useNavigate();
useEffect(() => {
init();
}, []);
const init = () => fetchServices();
const fetchServices = async () => {
const services = await HttpClient.get(`services`);
const vendors = await HttpClient.get(`vendor`);
setVednors(vendors);
setServices(services);
};
const handleChange = (event) => {
setSelectedVendor(event.target.value);
};
console.log(services);
return services.length === 0 ? (
<Grid>
<Typography
sx={{
fontSize: 32,
color: "blue",
display: "flex",
justifyContent: "center",
alignItems: "center",
textDecoration: "underline",
}}
>
Loading your services.....
</Typography>
</Grid>
) : (
<Grid container>
<ConfirmDialog />
<Grid container item xs={12} columnGap={2}>
<Box sx={{ float: "right" }}>
<Button
variant="outlined"
onClick={() => {
confirmDialog("Are you sure want to logout?", () => {
localStorage.clear();
navigate("/login");
});
}}
>
Logout
</Button>
</Box>
</Grid>
<Grid container item xs={12} rowGap={2}>
{services.map((service, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<Card>
<CardActionArea>
<CardContent>
<Grid
component="img"
sx={{
height: 150,
width: 350,
maxHeight: { xs: 150, md: 175 },
maxWidth: { xs: 350, md: 375 },
objectFit: "cover",
}}
alt="The house from the offer."
src={
PF +
service.photo +
"?alt=media&token=c19f2d0a-f254-4391-b589-ef7ee3cad9f5"
}
></Grid>
<Grid item xs={12}>
<Typography textAlign="center">
{service.service}
</Typography>
</Grid>
<Grid item xs={12}>
<FormControl sx={{ minWidth: 120 }} size="small" fullWidth>
<InputLabel id="demo-select-small">
Select Vendor
</InputLabel>
<Select
size="small"
labelId="demo-select-small"
id="demo-select-small"
value={selectedVendor}
label="Select Vendor"
onChange={handleChange}
>
{vendors.map((vendor) => (
<MenuItem
key={vendor._id}
value={vendor.name}
id={vendor.name}
>
{vendor.name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
</CardContent>
</CardActionArea>
<CardActions>
<Grid item xs={12}>
<Button variant="outlined" color="primary" fullWidth>
Book Service
</Button>
</Grid>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Grid>
);
};
export default Services;
I want the dropdown action reflect in the selected card.

It's because all of your mapped Select elements get their value from selectedVendor. you can change the code like this using these steps:
First:
Change the selectingVendor useState like this:
const [selectedVendors, setSelectedVendors] = useState([]);
Second:
And the handleChange function
const handleChange = (event,index) => {
const currentSelectedVendors = [...selectedVendors];
currentSelectedVendors[index] = event.target.value
setSelectedVendors(currentSelectedVendors);
};
Third:
You need to change the Select Mui component props.
<Select
size="small"
labelId="demo-select-small"
id="demo-select-small"
value={selectedVendors[index] || ""}
label="Select Vendor"
onChange={(e) => handleChange(e,index)}
>
Although there is another solution for this problem and you can pull <Select> logic out of Services component , I mean it's selectingVendor hook and it's change handler and create a new separate component which handles it.
PS: I think it should be better to use vendor._id as the value prop of your MenuItems

Related

mui Select/chip with Reach-hook-form - Can't get the update value (Reactjs)

I've succesfully implemented two seperate reusable MUI TextField and Select components , but having issue with third one, which contains both Mui Select/Chip in one single component, the code is two parts, one is the main component which call the second one,
/// Main component///
const { handleSubmit, reset, formState: { errors }, control } = useForm({
defaultValues: {
contractCode: 'sss', stores: [],
},
resolver: yupResolver(schema)
});
return (
.......
<Box m="1rem 0.7rem"
<FormInputText errors={errors} control={control} name='contractCode' label='Contract Code' />
<FormMultipleSelectChip errors={errors} control={control} name='stores' required label='Stores' />
</Box>
.......
);
/// Below is my Child component to re-use
const names = [
'Oliver Hansen',
'Virginia Andrews',
'Kelly Snyder',
];
export default function MultipleSelectChip({ label, inputProps, control, name, errors,stores }) {
const [personName, setPersonName] = React.useState([]);
const handleChange = (event) => {
const {
target: { value },
} = event;
setPersonName(
};
return (
<div>
<FormControl >
<Typography> Locations </Typography>
<Controller
name={name}
control={control}
render={({ field : { onChange, value, ...rest} }) => (
<Select
{...rest}
multiple
value={personName}
onChange={handleChange}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem key={name} value={name} >
{name}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
</div>
);
}
Once I submitted the the form, Im not getting the value for 'stores' as attached, but im getting proper value for rest of the fields, enter image description hereenter image description here
Appreciate if anyone help me to fix this issue
Thanks
Syed

Material UI Popover only hides when clicking away

I need some help...
I'm working with a material UI Popover: currently, it shows when hovering over a certain place and it will only hide when clicking anywhere away from it. I want it to hide automatically as soon as my mouse isn't hovering anymore.
Can you tell me what's wrong with my code?
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const openModal = Boolean(anchorEl);
const id = openModal ? "simple-popover" : undefined;
const handleClick = (event: any, params: any) => {
setOrganization({...params});
setAnchorEl(event.currentTarget);
console.log("hola", "Hola")
};
const handlePopover = () => {
setAnchorEl(null);
setOrganization({ idor: null });
};
<Grid container direction="column" height="60%">
<Grid item container justifyContent="center">
<Badge
style={{ cursor: "pointer" }}
badgeContent={params.row.active ? "Active" : "Inactive"}
color={params.row.active ? "success" : "error"}
onMouseEnter={(e: any) => handleClick(e, params.row)}
/>
<Popover
id={id}
onExit={handlePopover}
open={openModal}
anchorEl={anchorEl}
onClose={handlePopover}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
>
<Grid container justifyContent="center">
<Tooltip title="View Window">
<IconButton
onClick={() => handleToOrganization(params.row, "view")}
style={{ marginLeft: 16 }}
>
<LaunchOutlinedIcon />
</IconButton>
</Tooltip>
<Tooltip title="Edit">
<IconButton
onClick={() => handleToOrganization(params.row, "edit")}
style={{ marginLeft: 16 }}
>
<EditOutlinedIcon />
</IconButton>
</Tooltip>
<Tooltip title="Delete">
<IconButton
onClick={() => handleOpen()}
style={{ marginLeft: 16 }}
>
<DeleteOutlineOutlinedIcon />
</IconButton>
</Tooltip>
</Grid>
</Popover>
</Grid>
</Grid>```
Thank you in advance! :)

REACT 17 wrap mui tab in form how to control form submission with react hook form

I'm stuck can a charitable soul advise me : I have a form that wraps 3 tabs
which contain text fields
interface TabPanelProps {
// eslint-disable-next-line react/require-default-props
children?: ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
aria-labelledby={`simple-tab-${index}`}
hidden={value !== index}
id={`simple-tabpanel-${index}`}
role="tabpanel"
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<div>{children}</div>
</Box>
)}
</div>
);
}
function getTab(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
export default function tt({ closeModal, testData }: any) {
const [value, setvalue] = useState(0);
const methods = useForm<MyInterface>();
const {
register,
handleSubmit,
formState: { errors, isDirty },
} = methods;
const onSubmit = (formValue: MyInterface) => {
console.log("isSubmitted");
console.log(formValue);
};
const handleChangeTab = (event: SyntheticEvent, newValue: number) => {
setvalue(newValue);
};
return (
<FormProvider {...methods} >
< form className="myForm"
onSubmit={methods.handleSubmit(onSubmit)} >
<Box sx={{ width: '100%' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs aria-label="basic tabs example" onChange={handleChangeTab} value={value}>
<Tab label="1" {...getTab(0)} />
<Tab label="2" {...getTab(1)} />
<Tab label="3" {...getTab(2)} />
</Tabs>
</Box>
<TabPanel index={0} value={value}>
<Component1 />
</TabPanel>
<TabPanel index={1} value={value}>
<Component2 testData={testData} />
</TabPanel>
<TabPanel index={2} value={value}>
<Component3 />
</TabPanel>
</Box>
<DialogActions>
<Button onClick={methods.handleSubmit(onSubmit)}>Enregistrer</Button>
<Button onClick={closeModal}>Annuler</Button>
</DialogActions>
</form>
</FormProvider>
);
}
2 and 3 have for example juste textField which are required in Component 1 but not in Component 2 and 3 :
import { useFormContext } from "react-hook-form";
import { TextField } from '#mui/material';
export default function Component1() {
const { register, formState } = useFormContext();
return (
<>
<TextField
error={Boolean(formState.errors?.name)}
fullWidth
helperText={Boolean(formState.errors?.name) === false ? "give the name" : formState.errors.name?.message}
{...register('name', { required: "name is required." })}
label="name"
variant="standard"
/>
</>
);
}
code in the Component2:
import { Controller, useFormContext } from 'react-hook-form';
import { Checkbox, Chip, FormControl, InputLabel, ListItemText, MenuItem, Select, SelectChangeEvent, TextField } from '#mui/material';
export default function Component3(props: any) {
const { register, control } = useFormContext();
return (
<>
<FormControl sx={{ m: 1, minWidth: '100%' }} variant="standard">
<InputLabel id="test">En prod</InputLabel>
<Controller
control={control}
defaultValue=""
name="test"
render={({ field }) => (
<Select labelId="test" {...field}>
<MenuItem value="yes">yes</MenuItem>
<MenuItem value="no">no</MenuItem>
</Select>
)}
/>
</FormControl>
my problem is:
when I change tab without clicking in a field of the form, that is sent even if the required fields are not filled.
On the contrary when I click in any field and the required fields are not filled in isDirty remains true.
Which is normal.
But if I fill in the required fields and I change tab isDirty always stays true and I can't send the form.
Thank you for your help!!

#mui grid items of equal height

Using the React #mui package, I want to create a grid of items that all stretch to the same height as the tallest item. I've tried searching for similar questions but have not found a working solution, and using #mui v5. Here's my example, also found on Sandbox https://codesandbox.io/s/happy-moon-y5lugj?file=/src/App.js. I prefer a solution (if possible) using the #mui components rather than grid css directly. Also I cannot fix the number of columns, it needs to be responsive.
import * as React from "react";
import {
Box,
Card,
Container,
Grid,
Paper,
Table,
TableBody,
TableRow,
TableCell,
Typography
} from "#mui/material";
export default function App() {
const createTable = (rows) => {
return (
<Table>
<TableBody>
{[...Array(rows).keys()].map((n) => {
return (
<TableRow key={n}>
<TableCell>Aaaaa</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
};
return (
<Box p={3}>
<Container maxWidth="sm" height="100%">
<Grid container spacing={2} height="100%">
<Grid item height="100%">
<Paper height="100%" elevation={3} sx={{ p: 3 }}>
<Typography variant="h4">Something</Typography>
{createTable(5)}
</Paper>
</Grid>
<Grid item height="100%">
<Paper height="100%" elevation={3} sx={{ p: 3 }}>
<Typography variant="h4">More things</Typography>
{createTable(3)}
</Paper>
</Grid>
</Grid>
</Container>
</Box>
);
}
This is what I get now. I want the shorter item to be padded on the bottom so its the same height as the first.
Please use this code from the box downwards. You can read more about how to work with grid items on the mui website here.
const StackOverflow = () => {
const createTable = (rows) => {
return (
<Table>
<TableBody>
{[...Array(rows).keys()].map((n) => {
return (
<TableRow key={n}>
<TableCell>Aaaaa</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
);
};
return (
<Box p={3}>
<Container maxWidth="sm">
<Grid container spacing={2}>
<Grid item xs={6}>
<Paper elevation={3} sx={{ p: 3, height: "100%" }}>
<Typography variant="h4">Something</Typography>
{createTable(5)}
</Paper>
</Grid>
<Grid item xs={6}>
<Paper elevation={3} sx={{ p: 3, height: "100%" }}>
<Typography variant="h4">More things</Typography>
{createTable(3)}
</Paper>
</Grid>
</Grid>
</Container>
</Box>
);
};
Something that worked good for me was to use the following in the Papers sx prop:
{{height: "100%", display: "flex", alignItems: "center"}}

Material UI: Apply the fullwidth feature to the Textfield

I have a staff monitoring project that contains many components and among these components is "creating a workspace" and how clear in the picture I made a component and put many elements in it, but the problem is that the "TextField" I want it to be until the end of the page And although I put "Full Width", it is only complete in the middle.
How can I solve the problem?
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
},
resize:{
fontSize:24
}
}),
);
const Settings: FC = () => {
const classes = useStyles();
return (
<div className={classes.root}>
{/*<Container maxWidth="lg" style={{backgroundColor: 'red'}}>*/}
<Grid container direction="row">
<Grid item>
<Avatar style={{width: '5rem', height: '5rem'}} alt="Remy Sharp"
src="/static/images/avatar/1.jpg"/>
</Grid>
<Grid item >
<TextField
fullWidth
name="workspaceName"
placeholder="Workspace Name"
variant="standard"
style={{
paddingLeft: '1.4rem',
transition: ' all .2s cubic-bezier(.785,.135,.15,.86) 0s',
display: 'flex',
alignItems: 'center',
flexGrow: 1,
position: 'relative',
color: '#828588',
}}
InputProps={{
classes: {
input: classes.resize,
},
}}
defaultValue="nameeeee"
/>
</Grid>
</Grid>
<Grid container spacing={5} direction="row" mt={14}>
<Grid item xs>
<Button style={{
minWidth: '10rem',
fontSize: '1.5rem',
height: '44px',
fontWeight: 400,
textShadow: 'none',
color: '#fd71af',
border: 0,
background: 'none'
}}>Delete Workspace</Button>
</Grid>
<Grid item >
<Button
color="primary"
component={RouterLink}
to="/dashboard/workspaces/1"
variant="contained"
style={{
minWidth: '13rem',
minHeight: '4.3rem',
fontSize: '1.4rem',
backgroundColor: '#7b68ee',
borderRadius: 6,
marginLeft:'60rem'
}}
>
Saved
</Button>
</Grid>
</Grid>
{/*</Container>*/}
</div>
);
}
export default Settings;
The fullWidth attribute sets the width to 100% of its parent. Its parent (The <Grid> component) isn't occupying the full space. Add css style { flexGrow: 1 } to the <TextField>'s <Grid> parent or set the xs attribute for the <Grid>
Check this part of the docs for reference