Radio Button selection does not work with MUI library - reactjs

I am having a problem with radio button selection.
handleChange event is triggering but dot does not appear on selection.
The count of objects is happening , checked in console.log.
I need to render a radio button list , I am using map method to iterate through the values.
Community appreciate your help !
import * as React from 'react';
import {Box, FormControl, FormControlLabel, RadioGroup, Radio } from '#mui/material'
function RadioBtnItem(props) {
const {
id,
name
} = props;
const [position, setPosition] = React.useState('id');
const handleChange = event => {
console.warn(event.target.value)
setPosition(event.target.value)
};
return (
<Box>
<FormControl component='fieldset'
>
<RadioGroup
name='positions-group'
arial-labelledby='positions-group-label'
onChange={handleChange}
value={id}
>
<FormControlLabel
value={id}
control={<Radio />}
label={name}
checked={position === {id}}
key={id} />
</RadioGroup>
</FormControl>
</Box>
);
}
export { RadioBtnItem };

Related

How to reset Material UI Checkbox on Dialog close

I've created a Dialog component via Material UI, dynamically imported from another file.
It works fine, except the checkboxes (also created with Material UI) inside this Dialog do not reset after each time Dialog closes. They only reset on page refresh. Other types of input, such as text or password do reset themselves automatically.
Please notice that the Dialog component does not have keepMounted = true, and that's how it resets its inputs automatically. Because, this value is false by default.
Here is the code for the original Dialog modal component:
import React, {useState, useEffect} from "react";
import Button from "#material/react-button";
import Divider from "#material-ui/core/Divider";
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogContentText from "#material-ui/core/DialogContentText";
import DialogTitle from "#material-ui/core/DialogTitle";
import TextField from "#material-ui/core/TextField";
import FormGroup from "#material-ui/core/FormGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import Checkbox from "#material-ui/core/Checkbox";
const SearchModal = (props) => {
const [checkState, setCheckState] = useState({
checkedQuestions: true,
checkedAnswers: true,
checkedVerified: true,
checkedPending: true,
checkedDisputed: false
});
useEffect(() => {
if(
checkState.checkedQuestions === false &&
checkState.checkedAnswers === false
){
setCheckState({
...checkState,
checkedQuestions: true,
checkedAnswers: true
});
}
if(
checkState.checkedVerified === false &&
checkState.checkedPending === false &&
checkState.checkedDisputed === false
){
setCheckState({
...checkState,
checkedVerified: true,
checkedPending: true,
checkedDisputed: false
});
}
});
const checkSet = (event) => {
setCheckState({
...checkState,
[event.target.name]: event.target.checked
});
}
return(
<Dialog
open={props.searchOpen}
onClose={props.handleClose}
aria-labelledby="searchModalTitle"
aria-describedby="searchModalDescription"
id="searchModal"
>
<DialogTitle id="dialog">{"Search tolodire."}</DialogTitle>
<DialogContent>
<DialogContentText className="marginBottom-17" id="searchModalDescription">
Search for questions or answers.
</DialogContentText>
<TextField
required
type="search"
id="searchQuery"
label="Enter keywords or sentences"
placeholder="Required"
variant="outlined"
data-owner="searchModal"
autoFocus
/>
<DialogContentText className="marginTop-20 marginBottom-10">
Use filters to search in detail.
</DialogContentText>
<FormGroup row className="marginTop-5">
<FormControlLabel
control={
<Checkbox
color="default"
checked={checkState.checkedQuestions}
onChange={(e) => checkSet(e)}
name="checkedQuestions"
/>
}
label="Questions"
/>
<FormControlLabel
control={
<Checkbox
color="default"
checked={checkState.checkedAnswers}
onChange={(e) => checkSet(e)}
name="checkedAnswers"
/>
}
label="Answers"
/>
</FormGroup>
<Divider/>
<FormGroup row>
<FormControlLabel
control={
<Checkbox
color="default"
checked={checkState.checkedVerified}
onChange={(e) => checkSet(e)}
name="checkedVerified"
/>
}
label="Verified"
/>
<FormControlLabel
control={
<Checkbox
color="default"
checked={checkState.checkedPending}
onChange={(e) => checkSet(e)}
name="checkedPending"
/>
}
label="Pending Verification"
/>
<FormControlLabel
control={
<Checkbox
color="default"
checked={checkState.checkedDisputed}
onChange={(e) => checkSet(e)}
name="checkedDisputed"
/>
}
label="Disputed"
/>
</FormGroup>
</DialogContent>
<DialogActions>
<Button raised className="button regularButton font-body" onClick={props.handleClose}>
Search
</Button>
</DialogActions>
</Dialog>
);
}
export default SearchModal
I've already tried searching this issue on Google and StackOverflow, yet, I haven't found any solution. Any contribution is appreciated.
P.S: The handleClose const is on another file;
const [searchOpen, setSearchOpen] = useState(false);
const handleSearchOpen = () => {
setSearchOpen(true);
};
const handleClose = () => {
setSearchOpen(false);
};
I've found the solution myself.
In the same file for SearchModal, I've created another useEffect field to check if the modal is open or not. When I was trying the same inside the pre-existing useEffect field, it created infinite loop.
For the reset process, I'm assigning the default values from scratch independently from other consts.
I am aware that the implementation could be much better, with more code-reuse or dynamic elements. But, this is just a primitive solution until a better one appears any time soon.
So, the solution is:
useEffect(() => {
if(props.searchOpen === false){
setCheckState({
checkedQuestions: true,
checkedAnswers: false,
checkedVerified: true,
checkedPending: true,
checkedDisputed: false
});
}
}, [props.searchOpen]);

How can I use React Testing Library to simulate clicking on Material UI RadioGroup option?

How can I write a react testing library test script to validate clicking on a Material UI radio button? The below is a recreation of the issue I am having.
import React from 'react';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
export default function RadioButtonsGroup() {
const [value, setValue] = React.useState('female');
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<FormControl component="fieldset">
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup aria-label="gender" name="gender1" value={value} onChange={handleChange}>
<FormControlLabel value="female" control={<Radio />} label="Female" />
<FormControlLabel value="male" control={<Radio />} label="Male" />
<FormControlLabel value="other" control={<Radio />} label="Other" />
<FormControlLabel value="disabled" disabled control={<Radio />} label="(Disabled option)" />
</RadioGroup>
</FormControl>
);
}
and the test
import { render, screen, fireEvent } from "#testing-library/react";
import RadioButtonsGroup from "./demo";
test("can click Male radio option", () => {
render(<RadioButtonsGroup />);
const maleLabel = screen.getByLabelText(/Male/);
fireEvent.click(maleLabel);
expect(maleLabel).toBeChecked();
});
Hey – get by role should help you here. Try:
const button = screen.getByRole("radio", { name: "Male" });
This should give you access to the radio button with the lable as Male.
Then you can do
const leftClick = { button: 0 };
userEvent.click(button, leftClick);
expect(button.checked).toBe(true);
One thing I've found though is that the .checked property doesn't play nice with Typescript so if you're writing typescript, you may get warnings about .checked being non-existent

react-hook-form Controller issues

Hi im trying to do one form with react-hook-form and material-ui. I don't want to write Controller every time for all TextFields. Because of that i declare it in another file and call it in my form but its not working i didn't understand why, because in some videos that i watched is working. What is the problem and how i can fix it ?
Form Field
import React from 'react'
import { TextField, Grid } from '#material-ui/core'
import { useForm, Controller, useFormContext } from 'react-hook-form'
const FormField = ({name, label}) => {
const { control } = useForm()
return (
<Grid item xs={12} sm={6} >
<Controller
render = {({field}) =>
<TextField
{...field}
label={label} required
/>}
name={name}
control = {control}
defaultValue=""
/>
</Grid>
)
}
export default FormField
Adress Form
import React from 'react'
import { InputLabel, Select, MenuItem, Button, Grid, Typography, TextField } from '#material-ui/core'
import { useForm, FormProvider, Controller } from 'react-hook-form'
import FormField from './FormField'
import { Link } from 'react-router-dom'
const AdressForm = ({next}) => {
const {handleSubmit, control} = useForm()
return (
<>
<Typography variant="h6" gutterBottom>Shipping Address </Typography>
<form onSubmit={handleSubmit((data) => console.log(data) )}>
<Grid container spacing={3}>
<FormField name='firstName' label='First Name' required='required'/>
<FormField name='lastName' label='Last Name' />
<FormField name='email' label='Email' />
<FormField name='phoneNumber' label='Phone Number' />
</Grid>
<br/>
<div style={{ display: 'flex', justifyContent: 'space-between'}}>
<Button component={Link} to="/cart" variant="outlined">Back to Cart</Button>
<Button type="submit" variant="contained" color="primary">Next</Button>
</div>
</form>
</>
)
}
export default AdressForm
You must use one useForm hook for each form, in your code, you call useForm in every Field components, creating multiple independent form states, which leads to unexpected result.
What you need to do is to call useForm in the parent element and pass the dependencies (register, formState, error...) down the child components, so your form can have one unified state. If you have a deeply nested components, you can use useFormContext to pass the form context to the nested children easily:
import React from "react";
import { useForm, FormProvider, useFormContext } from "react-hook-form";
export default function App() {
const methods = useForm();
const onSubmit = data => console.log(data);
return (
<FormProvider {...methods} > // pass all methods into the context
<form onSubmit={methods.handleSubmit(onSubmit)}>
<NestedInput />
<input type="submit" />
</form>
</FormProvider>
);
}
function NestedInput() {
const { register } = useFormContext(); // retrieve all hook methods
return <input {...register("test")} />;
}

react - tick logic for material-ui radio button

I have a minimalist function whereby I'm providing two choices in the form of radio inputs: "bull" or "bear".
Full code below:
import React, { useState } from 'react';
import RadioButton from 'material-ui/RadioButton';
const styles = {
rootRadio: {
//left:"37%",
//position:'absolute'
}
}
function RadioComp() {
const [riskP, setRiskP] = useState("bull")
const handleRisk = (e) => {
setRiskP(e.target.value)
}
return (
<React.Fragment>
<h2>Risk profile</h2>
<div>
<span>Bull</span>
<RadioButton
style={styles.rootRadio}
value='bull'
checked={riskP==='bull'}
onChange={handleRisk}
/>
</div>
<div>
<span>Bear</span>
<RadioButton
style={styles.rootRadio}
value='bear'
checked={riskP==='bear'}
onChange={handleRisk}
/>
</div>
</React.Fragment>
)
}
export default RadioComp;
As seen above, I have tried to configure this logic using useState():
const [riskP, setRiskP] = useState("bull")
const handleRisk = (e) => {
setRiskP(e.target.value)
}
However, in the view, clicking on "bear" doesn't do anything. It seems to be locked on "bull."
Question
You have to use RadioGroup with FormControl option.
import React from 'react';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
export default function RadioButtonsGroup() {
const [value, setValue] = React.useState('female');
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<FormControl component="fieldset">
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup name="gender1" value={value} onChange={handleChange}>
<FormControlLabel value="female" control={<Radio />} label="Female" />
<FormControlLabel value="male" control={<Radio />} label="Male" />
<FormControlLabel value="other" control={<Radio />} label="Other" />
</RadioGroup>
</FormControl>
);
}

Uncheck Radio buttons with Material UI

I wish to be able to uncheck radio buttons, Idea is like this: if I click on some radio button, it is going to be checked, if I click on another field, this another field is going to be checked instead BUT if I click on field which is already checked, I wish to uncheck it so all fields are empty. I tried to catch the moment of being checked or unchecked but seems like opposite to checkboxes, Radio buttons don't have this field. does anyone has idea how to achieve that?
setTests = (key, e) => {
console.log(e.checked)
if (e.checked) {
// this.setState({[key]: null})
console.log('works')
}
}
RadioGroup
value={this.state.test_mode}
style={{ display: "block" }}
onChange={e => this.setTests({ "test_mode", e.target })}
>
<FormControlLabel value="before" control={<Radio color="primary"/>} label="before tests" />
<FormControlLabel value="progressing" control={<Radio color="primary"/>} label="progressing" />
<FormControlLabel value="done" control={<Radio color="primary"/>} label="done" />
</RadioGroup>
Below is an example of how to do this. Instead of using the onChange of the RadioGroup, you use the onClick event of the Radio. If the new value matches the current value in state, then set the value to empty string.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Radio from "#material-ui/core/Radio";
import RadioGroup from "#material-ui/core/RadioGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import FormControl from "#material-ui/core/FormControl";
import FormLabel from "#material-ui/core/FormLabel";
const useStyles = makeStyles(theme => ({
root: {
display: "flex"
},
formControl: {
margin: theme.spacing(3)
},
group: {
margin: theme.spacing(1, 0)
}
}));
export default function RadioButtonsGroup() {
const classes = useStyles();
const [value, setValue] = React.useState("female");
function handleClick(event) {
if (event.target.value === value) {
setValue("");
} else {
setValue(event.target.value);
}
}
return (
<div className={classes.root}>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup
aria-label="gender"
name="gender1"
className={classes.group}
value={value}
>
<FormControlLabel
value="female"
control={<Radio onClick={handleClick} />}
label="Female"
/>
<FormControlLabel
value="male"
control={<Radio onClick={handleClick} />}
label="Male"
/>
<FormControlLabel
value="other"
control={<Radio onClick={handleClick} />}
label="Other"
/>
</RadioGroup>
</FormControl>
</div>
);
}

Resources