react - tick logic for material-ui radio button - reactjs

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>
);
}

Related

Radio Button selection does not work with MUI library

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 };

REACT JS-How to randomly display the data from firestore?

I already have database for my "questions" and its "choices" now i want to display randomly.
import "./startExam.scss"
import HomeSidebar from '../homeSidebar/HomeSidebar'
import HomeNavbar from '../homeNavbar/HomeNavbar'
// import * as React from 'react';
import Radio from '#mui/material/Radio';
import RadioGroup from '#mui/material/RadioGroup';
import FormControlLabel from '#mui/material/FormControlLabel';
import FormControl from '#mui/material/FormControl';
import FormLabel from '#mui/material/FormLabel';
import { useParams , useNavigate } from 'react-router-dom';
import { getDocs, collection , onSnapshot, deleteDoc, doc , updateDoc } from '#firebase/firestore'
import { db } from '../../firebase'
import React, { useEffect, useState,useRef } from 'react'
const StartExam = () => {
const [getDocs,setgetDocs] = useState([])
const [getTitle,setTitle] = useState([])
let {id}=useParams()
const isMounted = useRef()
const collectionRef = collection(db, "Exams",id,"qna")
let navigate = useNavigate();
const getData = () => {
onSnapshot(collectionRef, (data) => {
setgetDocs(data.docs.map((doc) => {
return {...doc.data(), id: doc.id}
}))
})
}
useEffect(() => {
if(isMounted.current){
return
}
isMounted.current = true;
getData()
}, []);
return (
<div className="startExam">
<HomeSidebar/>
<div className="startExamContainer">
<HomeNavbar/>
{getDocs.map((row) => {
<div className="titleContainer" key={row.id}>
return(
<div className="examTitle">{row.title}</div>
)
</div>
})}
{getDocs.map((rows)=>{
return(
<div className="questionContainer">
<FormControl>
<FormLabel id="demo-radio-buttons-group-label">{rows.question}</FormLabel>
<RadioGroup
aria-labelledby="demo-radio-buttons-group-label"
defaultValue="female"
name="radio-buttons-group">
<FormControlLabel value="database" control={<Radio />} label={rows.correctanswer} />
<FormControlLabel value="data" control={<Radio />} label="Java" />
<FormControlLabel value="databytes" control={<Radio />} label="C##" />
<FormControlLabel value="databoard" control={<Radio />} label="Html" />
</RadioGroup>
</FormControl>
</div>
)
})}
<div className="questionContainer">
<FormControl>
<FormLabel id="demo-radio-buttons-group-label">2. Which programming language is not included?</FormLabel>
</FormControl>
</div>
</div>
</div>
)
}
export default StartExam
here is the database structure and this is where all the question and choices MUST randomly display
<RadioGroup
aria-labelledby="demo-radio-buttons-group-label"
defaultValue="female"
name="radio-buttons-group">
<FormControlLabel value="database" control={<Radio />} label={rows.correctanswer} />
<FormControlLabel value="data" control={<Radio />} label="Java" />
<FormControlLabel value="databytes" control={<Radio />} label="C##" />
<FormControlLabel value="databoard" control={<Radio />} label="Html" />
</RadioGroup>
as you can see in this line i just manually add the "correctanswer" which is not correct because when i add more questions, the correctanswer must be in one place. I also want the questions to be randomly displayed. i hope you can help me.

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")} />;
}

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