How to align Material ui textfields in React? - reactjs

I am developing a React js application using Material ui components.
I am a 2 column grid, each column has 4 textField.
Some of those textField are weapped in Autocomplete component.
The problem is that they don't have the same styles thus they are not aligned facing each others.
Here is a screenshot of the result
As you can notice the problem in the second and third row of the left column.
Here is my code
<Grid container style={{ width: "100%", marginTop: 10 }}>
<Grid item style={{ padding: 10 }} xs={12} sm={12} md={6}>
<TextField
className={`${classes.textfield} ${
values.nameError && classes.inputError
}`}
onFocus={(e) => setValues({ ...values, nameError: false })}
onChange={(e) => setValues({ ...values, name: e.target.value })}
error={values.nameError}
helperText={values.nameError ? t("Obligatoire") : ""}
margin="normal"
fullWidth
type="text"
value={values.name}
label={t("nomFR") + " *"}
autoComplete="given-name"
/>
//Same for the rest
</Grid>
<Grid item style={{ padding: 10 }} xs={12} sm={12} md={6}>
<SelectInputWrapper>
<FormControl fullWidth>
<Autocomplete
options={spaces}
getOptionLabel={(option) => option.label}
renderOption={(option, { selected }) => (
<div className={classes.menuItem}>{option.label} </div>
)}
value={values.espace}
disableClearable
renderInput={(params) => (
<TextField
{...params}
error={values.espaceError}
onFocus={(e) =>
setValues({ ...values, espaceError: false })
}
style={{ marginLeft: 3, marginRight: 3 }}
margin="normal"
value={""}
label={t("Espace") + " *"}
/>
)}
/>
{values.espaceError && (
<FormHelperText className={classes.helper} error={true}>
{t("Obligatoire")}
</FormHelperText>
)}
</FormControl>
</SelectInputWrapper>
//Same for the rest
</Grid>
</Grid>
Notice, I am not giving any margins or paddings.
Is there a solution please? I am stuck here for 2 days.

Related

How can I render input into Chip in material ui without multiple set?

<StyledAutocomplete
sx={{ width: "40%", ml: "10px", mr: "10px" }}
id='multiple-limit-tags'
options={locationOptions}
filterSelectedOptions
autoComplete={true}
onChange={handleLocationChange}
getOptionLabel={option => option.label}
freeSolo
autoHighlight
forcePopupIcon
autoFocus
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip key={index} variant='filled' label={option} {...getTagProps({ index })} />
))
}
renderInput={params => (
<TextField
{...params}
variant='outlined'
label='Location'
placeholder={selectedLocations.length < 1 ? "Country" : ""}
/>
)}
/>
I wanna be able to render the input into a chip without multiple prop, the renderTags props only seems to work on multiple

Handling Multiple Checkboxes with Material UI and Formik

I have a form with a checkbox group containing 4 checkboxes within. I need to pass the value to my api on submit of button which is to be handled through Formik.
There are a few conditions on clicking a particular checkbox:
If Height is checked, rest all should be checked as well
If Breadth is checked, except for Height all should be checked
If Length is checked, Both Nos and Length should be checked
Nos can be checked or left unchecked
Currently I'm declaring individual states for all checkboxes and declaring individual functions for each of the conditions mentioned above.
Is there any other way to bind the values from the checkbox to Formik and pass to the api
State and Individual Functions:
const [nos, setNos] = useState(false);
const [length, setLength] = useState(false);
const [breadth, setBreadth] = useState(false);
const [height, setHeight] = useState(false);
const handleHeight = e => {
setNos(!nos);
setLength(!length);
setBreadth(!breadth);
setHeight(!height);
};
const handleBreadth = e => {
setNos(!nos);
setLength(!length);
setBreadth(!breadth);
};
const handleLength = e => {
setNos(!nos);
setLength(!length);
};
const handleNos = e =>{
setNos(!nos);
};
Form with Formik:
const formik = useFormik({
initialValues,
validationSchema,
onSubmit: async (values) => {
alert(JSON.stringify(values, null, 2));
},
});
return (
<>
<form
onKeyDown={onKeyDown}
id="newJobDetails"
name="newJobDetails"
onSubmit={formik.handleSubmit}
className={CommonClasses.formPartStyling}
style={{
height: SetFormSize(
isAdd ? "Add" : textFieldDisable ? "Info" : "Edit",
1
),
marginTop: marginTop || "none",
}}
>
<div
className={CommonClasses.headingTextOfForms}
style={{ marginLeft: "4%", marginTop: "2%" }}
>
Add Jobs
</div>
<Divider
style={{
marginTop: "1%",
marginBottom: "3%",
color: "white",
}}
></Divider>
<div
style={{
marginLeft: "4%",
marginTop: "2%",
}}
>
<Grid container>
<Grid item xs={6}>
<LabelStyling>Job Name</LabelStyling>
<TextField
id="jobName"
name="jobName"
variant="outlined"
placeholder="Enter Job Name"
value={formik.values.jobName}
onChange={formik.handleChange}
size="small"
style={{
width: FORM_PART_VARS.INPUT_BOX_WIDTH,
}}
/>
</Grid>
<Grid item xs={6}>
<LabelStyling>Stage</LabelStyling>
<DropDown
id="stage"
width={FORM_PART_VARS.INPUT_BOX_WIDTH}
value={formik.values.stage}
onChange={formik.handleChange}
error={formik.touched.stage && Boolean(formik.errors.stage)}
helperText={formik.touched.stage && formik.errors.stage}
mapFile={Stage}
placeholder="Select Stage"
/>
</Grid>
</Grid>
</div>
<div
style={{
marginLeft: "4%",
marginTop: "2%",
}}
>
<Grid container>
<Grid item xs={6}>
<LabelStyling>Measurement</LabelStyling>
<TextField
id="measurement"
name="measurement"
variant="outlined"
placeholder="Enter Measurement"
value={formik.values.measurement}
onChange={formik.handleChange}
size="small"
style={{
width: FORM_PART_VARS.INPUT_BOX_WIDTH,
}}
/>
</Grid>
<Grid item xs={6}>
<LabelStyling>Measurement Unit</LabelStyling>
<DropDown
id="measurement_unit"
width={FORM_PART_VARS.INPUT_BOX_WIDTH}
value={formik.values.measurement_unit}
onChange={formik.handleChange}
error={
formik.touched.measurement_unit &&
Boolean(formik.errors.measurement_unit)
}
helperText={
formik.touched.measurement_unit &&
formik.errors.measurement_unit
}
mapFile={MeasurementUnit}
placeholder="Select Unit"
/>
</Grid>
</Grid>
</div>
<div
style={{
marginLeft: "4%",
marginTop: "2%",
}}
>
<Grid container>
<Grid item xs={6}>
<LabelStyling>Rate</LabelStyling>
<TextField
name="jobRate"
id="jobRate"
variant="outlined"
placeholder="Enter Rate"
value={formik.values.jobRate}
onChange={formik.handleChange}
size="small"
style={{
width: FORM_PART_VARS.INPUT_BOX_WIDTH,
}}
/>
</Grid>
<Grid item xs={6}>
<LabelStyling>Service Rate</LabelStyling>
<TextField
name="serviceRate"
id="serviceRate"
variant="outlined"
placeholder="Enter Service Rate"
value={formik.values.serviceRate}
onChange={formik.handleChange}
size="small"
style={{
width: FORM_PART_VARS.INPUT_BOX_WIDTH,
}}
/>
</Grid>
</Grid>
</div>
<div
style={{
marginLeft: "4%",
marginTop: "4%",
}}
>
<LabelStyling>Select Measurement(s)</LabelStyling>
<FormGroup row>
<FormControlLabel control={<CheckBox checked={nos} onChange={handleNos} name="Nos" id="1"/>} label="Nos" />
<FormControlLabel control={<CheckBox checked={length} onChange={handleLength} name="Length" id="2"/>} label="Length" style={{marginLeft: "15px"}}/>
<FormControlLabel control={<CheckBox checked={breadth} onChange={handleBreadth} name="Breadth" id="3"/>} label="Breadth" style={{marginLeft: "15px"}}/>
<FormControlLabel control={<CheckBox checked={height} onChange={handleHeight} name="Height" id="4"/>} label="Height" style={{marginLeft: "15px"}}/>
</FormGroup>
</div>
<div
style={{
marginLeft: "4%",
marginTop: "4%",
}}
>
<LabelStyling>Select Job Type</LabelStyling>
<FormGroup row>
<FormControlLabel control={<CheckBox name="jobType" value="Residential"/>} label="Residential"/>
<FormControlLabel control={<CheckBox name="jobType" value="Commercial"/>} label="Commercial"/>
<FormControlLabel control={<CheckBox name="jobType" value="Infrastructure"/>} label="Infrastructure"/>
</FormGroup>
</div>
</form>
</>
);
When using Formik , it is not needed to maintain additional state to capture the form field values Formik will take care of that .
In this case , since we need to programmatically change the values of other inputs based on a change in one input we can make use of the setFieldValue prop provided by Formik .
Working Sandbox
To combine multiple states and multiple handlers, try something like this:
const [state, setState] = useState({
nos: false,
length: false,
breadth: false,
height: false
});
const handleCheckBox = useCallback((checkbox) => () => setState(state => {
switch (checkbox) {
case 'nos': return {
...state,
nos: !state.nos,
};
case 'length': return {
...state,
nos: !state.nos,
length: !state.length,
};
case 'breadth': return {
...state,
nos: !state.nos,
length: !state.length,
breadth: !state.breadth,
};
case 'height': return {
...state,
nos: !state.nos,
length: !state.length,
breadth: !state.breadth,
height: !state.height,
};
default: {
console.warn('unknown checkbox', checkbox);
return state;
}
}
}), []);
<FormGroup row>
<FormControlLabel control={<CheckBox checked={state.nos} onChange={handleCheckBox('nos')} name={'Nos'} id={'1'}/>} label={'Nos'}/>
<FormControlLabel control={<CheckBox checked={state.length} onChange={handleCheckBox('length')} name={'Length'} id={'2'}/>} label={'Length'} style={{marginLeft: '15px'}}/>
<FormControlLabel control={<CheckBox checked={state.breadth} onChange={handleCheckBox('breadth')} name={'Breadth'} id={'3'}/>} label={'Breadth'} style={{marginLeft: '15px'}}/>
<FormControlLabel control={<CheckBox checked={state.height} onChange={handleCheckBox('height')} name={'Height'} id={'4'}/>} label={'Height'} style={{marginLeft: '15px'}}/>
</FormGroup>

How can I change the way of selecting in a checkbox?

I'm using react and I have a checkbox created with Autocomplete where I select items. It
is the same as that provided by material:
The problem is that the function "OnChange" runs only if I select the square.
I would like it to work also by selecting the row. How can I do it? Here's my code:
<AutoComplete
multiple
id="checkboxes-tags-demo"
options={props.roles}
// disableCloseOnSelect
// getOptionLabel={(options) => `${options}`}
renderOption={(options, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 20 }}
value={options}
checked={selected}
onChange={updateNewRoles}
/>
{options}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label=""
placeholder="Available Roles"
/>
)}
/>
<button type="submit" onClick={onSubmitChecked}>
Save
</button>
This is a photo of the example provided by material. The functions are performed only by clicking the icon and not the line:

Search button to activate autocomplete googl maps places (React.js)

I am using AutoComplete button from material/ui for searching Google Maps Places. Everything is working properly so when user clicks on the one offered field in autocomplete it leads him to next page.
My question is: is it possible to add search button to active autocomplete if user clicks on button?
Here is code for autocomplete and I have added button but still nothing happens if someone clicks on it.
<div className="search">
<Autocomplete
id="google-map-demo"
key={reset}
ListboxProps={{ style: { maxHeight: 400, overflow: 'auto',fontSize:'0.7rem' } }}
getOptionLabel={(option) =>
typeof option === "string" ? option : option.description
}
filterOptions={(x) => x}
options={options}
onChange={(e, l) => {
customer.setMyaddress(l.description);
getMyDestination(l.place_id);
}}
renderInput={(params) => (
<TextField
{...params}
className={classes.root}
variant="standard"
fullWidth
onChange={(e, v) => {
setInputValue(e.target.value);
}}
/>
)}
renderOption={(option) => {
const matches =
option.structured_formatting.main_text_matched_substrings;
const parts = parse(
option.structured_formatting.main_text,
matches.map((match) => [
match.offset,
match.offset + match.length,
])
);
return (
<Grid container alignItems="center">
<Grid item>
<LocationOnIcon className={classes.icon} />
</Grid>
<Grid item xs>
{parts.map((part, index) => (
<span
key={index}
style={{ fontWeight: part.highlight ? 700 : 400 }}
>
{part.text}
</span>
))}
<Typography variant="body2" color="red" className={classes.noOptions} >
{option.structured_formatting.secondary_text}
</Typography>
</Grid>
</Grid>
);
}}
/>
<button className="btn btn-primary" style={{position: "absolute",
right: "0"}}
onClick={(e, v) => {
setInputValue(e.target.value);
}}>Find</button>
</div>
Anyone has idea how to fix this so when clicked on button I got same result as clicked on autocomplete field?
Thank you
you use the open prop which is in the Autocomplete , i've forked the Autocomplete example from material-ui check it out:
https://codesandbox.io/s/material-demo-forked-3n5lw?file=/demo.js

How to update state of a component which uses two context consumers

I have a class component which uses two contexts with the value generated from API calls to a REST API.
What I want to do is get the context values and use them to update my component state.
I'm passing the context values like so
<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => {
const text = textContext.text;
const user = userConsumer.user;
if(text != null && user != null){
return (
<div className="md:flex max-w-2xl">
<div className="flex flex-col flex-1 md:pr-32">
<FuseAnimateGroup
enter={{
animation: "transition.slideUpBigIn"
}}
>
<div style={{paddingRight:"8px"}}>
<Typography variant="h4" >{text.TITLE_PAGE_PROFILE}</Typography>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
placeholder={text.PROFILE_EMAIL_PLACEHOLDER}
value = {user.email}
disabled
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment>
<IconButton>
<EmailIcon/>
</IconButton>
</InputAdornment>
)
}}
/>
</div>
<div style={{paddingRight:"8px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
placeholder={text.PROFILE_NAME}
value={user.name_user}
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<AccountCircle />
</InputAdornment>
)
}}
/>
</form>
</div>
<div style={{paddingRight:"8px"}}>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
value={user.address_user}
placeholder={text.PROFILE_ADDRESS_PLACEHOLDER}
fullWidth
margin="normal"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
</div>
<div style={{paddingRight:"8px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
value={user.city_user}
label={text.PROFILE_CITY_PLACEHOLDER}
className={classes.textField}
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<LocationCityIcon/>
</InputAdornment>
)
}}
/>
</form>
</div>
<div>
<TextField
id="outlined-select-currency"
select
value={user.country_user}
label={text.PROFILE_COUNTRY_PLACEHOLDER}
InputProps={{
endAdornment: (
<InputAdornment>
<IconButton>
<FlagIcon/>
</IconButton>
</InputAdornment>
)
}}
fullWidth
style={{ margin: 8, paddingRight: 8}}
SelectProps={{
MenuProps: {
className: classes.menu,
},
}}
margin="normal"
variant="outlined"
/>
</div>
<div style={{padding:"10px"}}>
<Fab variant="contained" aria-label="delete" className={classes.fab}>
{text.PROFILE_CHANGE_PASSWORD_BUTTON_PLACEHOLDER}
</Fab>
</div>
<div style={{paddingRight:"8px"}}>
<Typography variant="h4" > {text.COMPANY_INFORMATION_TITLE}</Typography>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
placeholder={text.COMPANY_NAME_PLACEHOLDER}
value={user.name_company}
fullWidth
margin="normal"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
</div>
<div style={{paddingLeft:"10px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
style={divStyle}
id="outlined"
label={text.COMPANY_EU_VAT_PLACEHOLDER}
value={user.vat_company}
className={classes.textField}
margin="normal"
variant="outlined"
/>
<TextField
style={div2Style}
id="outlined"
label={text.COMPANY_NUMBER_PLACEHOLDER}
value={user.registration_number_company}
className={classes.textField}
margin="normal"
variant="outlined"
/>
</form>
</div>
<div style={{paddingRight:"8px"}}>
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
value={user.address_company}
placeholder={text.COMPANY_ADDRESS_PLACEHOLDER}
fullWidth
margin="normal"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
</div>
<div style={{paddingRight:"8px"}}>
<form className={classes.container} noValidate autoComplete="off">
<TextField
id="outlined-full-width"
style={{ margin: 8 }}
label={text.COMPANY_CITY_PLACEHOLDER}
value={user.city_company}
className={classes.textField}
fullWidth
margin="normal"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<LocationCityIcon/>
</InputAdornment>
)
}}
/>
</form>
</div>
<div>
<TextField
id="outlined-select-currency"
select
label={text.COMPANY_COUNTRY_PLACEHOLDER}
fullWidth
style={{ margin: 8, paddingRight: 8}}
SelectProps={{
MenuProps: {
className: classes.menu,
},
}}
InputProps={{
endAdornment: (
<InputAdornment>
<IconButton>
<FlagIcon/>
</IconButton>
</InputAdornment>
)
}}
margin="normal"
variant="outlined"
/>
</div>
</FuseAnimateGroup>
</div>
<div className="flex flex-col md:w-320">
<FuseAnimateGroup
enter={{
animation: "transition.slideUpBigIn"
}}
>
<Button variant="contained" size="large" color="default" className={classes.button}>
{text.UPDATE_BUTTON_TEXT}
</Button>
</FuseAnimateGroup>
</div>
</div>
);
} else return <div>Loading...</div>
}
}
</UserContext.Consumer>
)}
</TextContext.Consumer>
I've tried to update the state inside the render by doing something like this
<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => {
const text = textContext.text;
const user = userConsumer.user;
this.setState({
user:user,
text: text,
})
</UserContext.Consumer>
)}
</TextContext.Consumer>
The problem with this approach is that it throws the "Maximum update depth exceeded." error.
How should I go about this?
"Maximum update depth exceeded." error.
Do not setState() inside render().
How should I go about this?
Simply extract a component from it.
const User = (props) => {
return (
<>
<span>{props.user}</span>
<span>{props.text}</span>
</>
);
}
// in render
<TextContext.Consumer>
{(textContext) => (
<UserContext.Consumer>
{(userConsumer) => (
<User
text={textContext.text}
user={userConsumer.user}
/>
))}
</UserContext.Consumer>
)}
</TextContext.Consumer>
<User /> will still re-render every time the props (user, text) changes.
you can't update the state inside the render function.
Like that, you will be in the infinity loop of renders. Whenever you change the state that triggers the render function then you change the state again and so on.
anyway, you don't need to store this state inside the local state to use it, you can use it directly from the context.
First of all - are you sure you really need to store context in state? I don't see any reason to copy context (which always available) to state. Just use values from context, not from state.
But if you really need it, you can't update state in render function, because it will cause the infinite update loop. There some options to do so:
Extract component:
return (
<TextContext.Consumer>
{({ text }) => (
<UserContext.Consumer>
({user}) => <ExtractedComponent text={text} user={user} />
</UserContext.Consumer>
)}
</TextContext.Consumer>
);
Then you just need to overrider getDerrivedStateFromProps() for ExtractedComponent to get new state when props changed.
[ugly way] perform conditional update in render function to prevent infinite loop:
if (state.user !== user || state.text !== text) {
this.setState({ user, text });
}
Probably you can switch to functional components with hooks:
const YourComponent = () => {
const { user } = useContext(UserContext);
const { text } = useContext(TextContext);
const [ state, setState ] = useState({});
useEffect(() => {
setState({ user, text });
}, [ user, text ]);
}

Resources