Testing React hook from and yup validation using jest and enzyme - javascript

I am trying to test this form component(I have used react-bootstrap for UI).
const validationSchema =
Yup.object().shape({
username: Yup.string().required(),
password: Yup.string().required(),
})
const formOptions = { resolver: yupResolver(validationSchema) };
// get functions to build form with useForm() hook
const { register, handleSubmit, reset, formState } = useForm(formOptions);
const { errors } = formState;
function onSubmit(data) {
console.log(JSON.stringify(data));
return (
<div
data-test="reg-login-component"
className="bg-dark pt-4"
style={{ height: "100vh" }}
>
<Card style={{ width: "50vw", margin: "auto" }}>
<Card.Body>
<Card.Title>Login</Card.Title>
<Form noValidate onSubmit={handleSubmit(onSubmit)}>
<Form.Row>
<Form.Group as={Col} md="12" sm="12" xs="12">
<Form.Label>UserName</Form.Label>
<Form.Control
name="username"
type="text"
{...register("username")}
/>
<Card.Subtitle className="mb-2 text-danger mt-0">
{" "}
{errors.username?.message}
</Card.Subtitle>
</Form.Group>
</Form.Row>
<Container fluid className="pr-0 pl-0">
<Form.Row>
<Form.Group as={Col}>
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
type="password"
{...register("password")}
/>
<Card.Subtitle className="mb-2 text-danger mt-0">
{" "}
{errors.password?.message}
</Card.Subtitle>
</Form.Group>
</Form.Row>
</Container>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</Card.Body>
</Card>
</div>
)};
Registration.test.js
Enzyme.configure({ adapter: new EnzymeAdapter() });
const setup = ()=> {
return mount(<Registration />);
};
describe("Checking valid inputs", () => {
it("submits input values", async () => {
const onSubmit = jest.fn();
const handleUserData = jest.fn();
const rendered = setup();
await act(async () => {
rendered
.find('input[name="username"]')
.simulate("input", { value: "harshitsankhala" })
.simulate("blur");
});
await act(async () => {
rendered.find("form").simulate("submit");
});
expect(onSubmit).toHaveBeenLastCalledWith(
expect.objectContaining({
username: "harshitsankhala",
})
);
});
});
when running npm test
getting this error ->
Checking valid inputs
× submits input values (81 ms)
● Checking valid inputs › submits input values
expect(jest.fn()).toHaveBeenLastCalledWith(...expected)
Expected: ObjectContaining {"username": "harshitsankhala"}
Number of calls: 0
58 | });
59 |
> 60 | expect(onSubmit).toHaveBeenLastCalledWith(
| ^
61 | expect.objectContaining({
62 | username: "harshitsankhala",
63 | })
i have used this code as a refrence for testing ->https://codesandbox.io/s/react-hook-form-enzyme-testing-example-5tn7i?from-embed=&file=/src/tests/app.test.js
How to resolve this issue??
or
How to perform test for react-hook-form and yup validation using enzyme and jest

Related

How to send data from Modal, Form using react and bootstrap?

I am working with React to POST data in my local database. When I use just the <form>, it works fine. But whenever I am trying to use Modal and Bootstrap, it is giving me an error. I understand that my handleChange/handleSubmit is probably not assigning the values. Just wondering how to send the data from Modal to the handleChange.
Here is my code:
constructor(props) {
super(props);
this.state = {
institutionName: {},
institutionAddress: {},
institutionPhone: {},
allData: [],
};
this.onSubmit = this.handleSubmit.bind(this);
}
// this will look at the API and save the incoming data in allData variable
componentDidMount() {
fetch("/manageInstitution")
.then((res) => res.json())
.then((data) => this.setState({ allData: data }))
.then(console.log(this.state.allData));
}
// this is when submit button is pressed and data will be sent to database using api
handleSubmit(event) {
event.preventDefault();
const data = {
institutionName: this.state.institutionName,
institutionAddress: this.state.institutionAddress,
institutionPhone: this.state.institutionPhone,
};
fetch("/manageInstitution", {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
})
.then((res) => res.json())
.catch((error) => console.error("Error: ", error))
.then((response) => console.log("Success: ", response));
window.location.reload(false);
}
// when a field changes in the form, do assignment to the state
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value.trim(),
});
};
handleModalShowHide() {
this.setState({ showHide: !this.state.showHide });
}
render() {
const { allData } = this.state;
return (
<div className="body">
<h4>Super Admin View</h4>
<div>
<Button variant="primary" onClick={() => this.handleModalShowHide()}>
Add New Institution
</Button>
<Modal
aria-labelledby="contained-modal-title-vcenter"
show={this.state.showHide}
onSubmit={this.handleSubmit}
>
<Modal.Header
closeButton
onClick={() => this.handleModalShowHide()}
>
<Modal.Title id="contained-modal-title-vcenter">
<h5>Add New Institution</h5>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form className="col-lg-6 offset-lg-3">
<Form.Group className="mb-3">
<Form.Label htmlFor="institutionName">
Institution Name:
</Form.Label>
<Form.Control
type="text"
size="sm"
name="institutionName"
placeholder="Enter Institution Name"
onChange={this.handleChange}
required
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label htmlFor="institutionAddress">
Institution Address:
</Form.Label>
<Form.Control
type="text"
size="sm"
name="institutionAddress"
placeholder="Enter Institution Address"
onChange={this.handleChange}
required
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label htmlFor="institutionPhone">
Institution Phone:
</Form.Label>
<Form.Control
type="tel"
size="sm"
name="institutionPhone"
placeholder="i.e 01911223344"
onChange={this.handleChange}
pattern="[0-9]{3}"
required
/>
</Form.Group>
</Form>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => this.handleModalShowHide()} >Close </Button>
{" "}
<Button
className="btn btn-primary"
type="submit"
onClick={this.handleSubmit}>Submit</Button>
</Modal.Footer>
</Modal.Body>
</Modal>
</div>
);
}
}
export default addNewInstitution;
Here is the error:
TypeError: Cannot read properties of undefined (reading 'state')
handleSubmit
D:/eduBD-React/eduBD/client/src/components/superadmin/manageInstitution.jsx:30
27 | event.preventDefault();
28 |
29 | const data = {
> 30 | institutionName: this.state.institutionName,
31 | institutionAddress: this.state.institutionAddress,
32 | institutionPhone: this.state.institutionPhone,
33 | };
You need to bind or use an Arrow function to have this to point to
correct reference refer docs here
class App extends React.Component {
state = {
name: "hello"
};
handleChangeWithArrow = () => { // use this way to avoid such errors
console.log(this.state.name);
this.setState({ name: "helloo Im changed" });
};
handleChange() {
// need to bind this to work or use an arrow function
console.log(this.state.name);
this.setState({ name: "helloo Im changed" });
}
render() {
return (
<div>
<button onClick={this.handleChange}>Error Produced on Click</button>
<button onClick={this.handleChangeWithArrow}>
Click Me to change Name
</button>
{this.state.name}
</div>
);
}
}
ReactDOM.render(<App/>,document.querySelector('#root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Mock function not being called using jest and testing library on react app

I am using jest and testing library to test a form. But the mock function is not being called. And the last line expect(mockOnSubmit).toHaveBeenCalled(); always fails.
Here's my test code:
App.test.js
import { render, fireEvent, cleanup } from "#testing-library/react";
import App from "./App";
import { act } from "react-dom/test-utils";
afterEach(cleanup);
test("email and password field are clear for submit", async () => {
const mockOnSubmit = jest.fn();
const { getByPlaceholderText, getByText } = render(
<App onSubmit={mockOnSubmit} />
);
const emailNode = getByPlaceholderText(/email/i);
const passwordNode = getByPlaceholderText(/password/i);
const testEmail = "test#example.com";
const testPassword = "123123";
await act(async () => {
fireEvent.change(emailNode, {
target: { value: testEmail }
});
fireEvent.change(passwordNode, {
target: { value: testPassword }
});
});
expect(emailNode.value).toBe(testEmail);
expect(passwordNode.value).toBe(testPassword);
const submit = getByText(/submit/i);
await act(async () => {
fireEvent.click(submit);
});
expect(mockOnSubmit).toHaveBeenCalled();
});
App.js
import { useForm } from "react-hook-form";
import { Controller } from "react-hook-form";
export default function App({ onSubmit = (data) => console.log(data) }) {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="email"
control={control}
defaultValue=""
rules={{
required: `Email is required.`,
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Invalid email address"
}
}}
render={({ field }) => {
return (
<input
placeholder="Email"
type="text"
key="email"
name="email"
id="email"
{...field}
/>
);
}}
/>
<Controller
name="email"
control={control}
defaultValue=""
rules={{
required: `Password is required.`
}}
render={({ field }) => {
return (
<input
placeholder="Password"
type="text"
key="password"
name="password"
id="password"
{...field}
/>
);
}}
/>
<input type="submit" value="submit" />
</form>
);
}
This minimal implementation is also available at
https://codesandbox.io/s/react-hook-form-testing-olo4i
Sidenote: This works fine without react-hook-form implementation. But as I add handleSubmit from react-hook-form it does not work anymore. Here's a working version by #Florian Motteau where it is working by bypassing react-hook-form
You are checking that the onSubmit props has been called, but in your component your are not calling this props when the form is submitted. So the mock you provide in the test will never get called. Besides this, in App.js, the onSubmit props is passed an evaluation of handleSubmit (handleSubmit()), instead you must pass a reference to the handler function (handleSubmit, or () => handleSubmit()).
Solving these two problems you get :
// App.js
export default function App({ onSubmit = (data) => console.log(data) }) {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={onSubmit}>
<Box sx={{ mt: 2 }}>
<EmailField label="Email" name="email" control={control} />
</Box>
<Box sx={{ mt: 2 }}>
<PasswordField label="Password" name="password" control={control} />
</Box>
<Box sx={{ mt: 2 }}>
<Button name="submit" type="submit" variant="contained">
Submit
</Button>
</Box>
</form>
);
}
I also suggets that you use visual text to interact with DOM elements :
fireEvent.click(getByText("Submit"));
Fixed sandbox : https://codesandbox.io/s/react-hook-form-testing-forked-vs9ez?file=/src/App.js
Finally, you should not have to wrap things in act(), react-testing-library will do this for you, see https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning.

ReactJs with Bootstrap: Multistep form validation, using Formik and Yup

I'm developing a multistep form in ReactJs with Bootstrap, using Formik and Yup.
Actually, I have 2 problems here, despite I've read part of Formik and Yup documentation, I haven't found the answer yet.
I change my select fields' values, but the validationSchema simply ignores these values and only the initialValues are submitted to this validation.
Before validating my fields' values through validationSchema, I have to check whether the select option is not the first one (this first option is equivalent to "choose your option" for each field, and when the user submits it, it's value should be empty for the schema), it's possible to be done without validationSchema, by creating a validate function out of Formik, but I want to use Formik because I want a prettier way to display the errors as it does. I've tried plenty of stuff around, but I haven't gotten any answer until now.
Here is the Step 1 Form Code**
import React, { Component } from 'react';
import Form from 'react-bootstrap/Form';
import {ErrorMessage, Formik, validateYupSchema} from "formik";
import Col from 'react-bootstrap/Col';
import InputGroup from 'react-bootstrap/InputGroup';
import Button from 'react-bootstrap/Button';
import * as yup from 'yup';
import PropTypes from 'prop-types';
import {Categories} from './categoriesList.jsx';
import {Brands} from './brandsList';
import {ProductTypes} from './productTypesList';
import NewProduct from './newProductComponent.jsx';
let schema = yup.object().shape({
productCategory: yup.string().required(),
productBrand: yup.string().required(),
productType: yup.string().required()
});
class FormProducts1 extends Component {
reset = () => {
this.props.handleReset1();
}
render() {
const {values, handleChange, handleReset1} = this.props;
const CategoryOptions = Categories,
MakeItemCategory = function(itemCategory) {
return <option value={itemCategory} key={itemCategory}>{itemCategory}</option>;
};
const BrandOptions = Brands,
MakeItemBrand = function(itemBrand) {
return <option value={itemBrand} key={itemBrand}>{itemBrand}</option>;
};
const ProductOptions = ProductTypes,
MakeItemProduct = function(itemProduct) {
return <option value={itemProduct} key={itemProduct}>{itemProduct}</option>;
};
return (
<Formik
initialValues = {{
productCategory:'',
productBrand: '',
productType: ''
}}
validationSchema = {schema}
onSubmit = {
values => {console.log(values)}
}
>
{({
handleSubmit,
handleBlur,
touched,
isInvalid, //defines what is invalid
errors //catch up the errors to be displayed
}) => (
<Form onSubmit={handleSubmit}>
<Form.Group as={Col} md="12" controlId="formProduct.ProductCategory">
<Form.Label>Categoria do Produto</Form.Label>
<Form.Control
name="productCategory"
as="select"
onChange={handleChange('productCategory')}
value={values.productCategory}
onBlur={() => handleBlur('productCategory',true)}
isInvalid={touched.productCategory && errors.productCategory}
>
{CategoryOptions.map(MakeItemCategory)}
</Form.Control>
<Form.Control.Feedback type="invalid">{errors.productCategory}</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="formProduct.ProductBrand">
<Form.Label>Marca do Produto</Form.Label>
<Form.Control
name="productBrand"
as="select"
onChange={handleChange('productBrand')}
value={values.productBrand}
onBlur = {() => handleBlur('productBrand',true)}
isInvalid = {touched.productBrand && errors.productBrand} >
{BrandOptions.map(MakeItemBrand)}
</Form.Control>
<Form.Control.Feedback type="invalid">{errors.productBrand}</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="12" controlId="formProduct.ProductType">
<Form.Label>Tipo do Produto</Form.Label>
<Form.Control
name="productType"
as="select"
onChange={handleChange('productType')}
value={values.productType}
onBlur = {() => handleBlur('productType',true)}
isInvalid={touched.productType && errors.productType}>
{ProductOptions.map(MakeItemProduct)}
</Form.Control>
<Form.Control.Feedback type="invalid">{errors.productType}</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="buttonsCategory" style={this.formatForm}>
<Button variant="secondary m-2" type="reset" style={this.styleAddButton} onClick={this.reset}>
Limpar
</Button>
<Button variant="primary" type="submit" style={this.styleAddButton}>
Continuar
</Button>
</Form.Group>
</Form>
)}
</Formik>
);
}
}
export default FormProducts1;
*****************Here is the Steps' Container Component Code *******************
import React, { Component } from 'react';
import FormProducts1 from './formProducts1';
import FormProducts2 from './formProducts2';
import FormProducts3 from './formProducts3';
import FormProductsSuccess from './formProductsSuccess';
import PropTypes from "prop-types";
class NewProduct extends Component {
state = {
step:1,
productCategory:'',
productBrand:'',
productType:'',
productName:'',
productPrice:0,
productCode:'',
productDescription:''
}
formatForm = {
marginTop:15
};
styleAddButton = {
fontSize:15,
fontWeight:"bold"
};
//Proceed to the next step
nextStep = () => {
const {step} = this.state
this.setState({
step: step + 1
});
}
//Go back to previous step <div>
prevStep = () => {
const {step} = this.state
this.setState({
step: step - 1
});
}
//Handle fields change
handleChange = input => e => {
this.setState({
[input]: e.target.value
});
}
toCurrency(number) {
const formatter = new Intl.NumberFormat("pt-BR", {
style: "decimal",
currency: "BRL"
});
return formatter.format(number);
}
handleReset1 = () => {
this.setState({
productCategory:'',
productBrand:'',
productType:''
});
}
handleReset2 = () => {
this.setState({
productName:'',
productPrice:0,
productCode:'',
productDescription:''
});
}
render() {
const {step} = this.state;
const {productCategory, productBrand, productType, productName, productPrice, productCode, productDescription} = this.state;
const values = {productCategory, productBrand, productType, productName, productPrice, productCode, productDescription};
switch(step){
case 1:
return (
<FormProducts1 nextStep = {this.nextStep}
handleChange = {this.handleChange} handleReset1 = {this.handleReset1}
values={values}
/>
)
case 2:
return(
<FormProducts2 nextStep = {this.nextStep}
prevStep = {this.prevStep} handleChange = {this.handleChange}
handleReset2 = {this.handleReset2}
values={values} toCurrency = {this.toCurrency}
/>
)
case 3:
return(
<FormProducts3 nextStep = {this.nextStep} prevStep = {this.prevStep}
values={values} toCurrency = {this.toCurrency}
/>
)
case 4:
return(
<FormProductsSuccess/>
)
}
}
}
NewProduct.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func
};
export default NewProduct;
Thanks!
It took two days to make it error-free... and I had hoped this would be work at your end..
Here, you can find react-bootstrap all input(form-control) along with formik and yup validation package
import React from 'react';
import { Container, Row, Col, Image, Form, Button } from 'react-bootstrap';
import style from '../styles/Contact.module.css';
import { Formik, Field} from 'formik';
import * as Yup from 'yup';
const dropdown=[
{
key:"Select an option",
value:""
},
{
key:"Option 1",
value:"option1"
},
{
key:"Option 2",
value:"option2"
},
{
key:"Option 3",
value:"option3"
}
]
// RegEx for phone number validation
const phoneRegExp = /^(\+?\d{0,4})?\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{4}\)?)?$/
// Schema for yup
const validationSchema = Yup.object().shape({
name: Yup.string()
.min(2, "*Names must have at least 2 characters")
.max(30, "*Names can't be longer than 30 characters")
.required("*Name is required"),
email: Yup.string()
.email("*Must be a valid email address")
.max(100, "*Email must be less than 100 characters")
.required("*Email is required"),
phone: Yup.string()
.min(10, "*Names can't be longer than 10 numbers")
.matches(phoneRegExp, "*Phone number is not valid")
.required("*Phone number required"),
msg: Yup.string()
.min(2, "*Messages must have at least 2 characters")
.max(250, "*Messages can't be longer than 250 characters")
.required("*Messages is required"),
selectionOption: Yup.string()
// .of(Yup.string())
// .min(1)
.required('Required'),
});
const Signup = () => {
return (
<>
<Formik
initialValues={{ name: "", email: "", phone: "", msg: "",selectionOption:"" }}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting, resetForm }) => {
// When button submits form and form is in the process of submitting, submit button is disabled
console.log(values)
setSubmitting(true);
// Simulate submitting to database, shows us values submitted, resets form
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
resetForm();
setSubmitting(false);
}, 500);
}}
>
{({ values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting }) => (
<Form className="form" onSubmit={handleSubmit} autoComplete="off" name="contact" method="POST" >
<Row className="mb-5">
<Col lg={6} md={6} sm={12}>
<Form.Group controlId="formName">
<Form.Label className="form_label" >FullName</Form.Label>
<Form.Control
type="text"
name="name"
placeholder="Full Name"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
className={touched.name && errors.name ? "has-error" : null}
/>
{touched.name && errors.name ? (
<div className="error-message">{errors.name}</div>
) : null}
</Form.Group>
</Col>
<Col lg={6} md={6} sm={12}>
<Form.Group>
<Form.Label className="form_label" >Number</Form.Label>
<Form.Control
type="text"
name="phone"
placeholder="Phone"
onChange={handleChange}
onBlur={handleBlur}
value={values.phone}
className={touched.phone && errors.phone ? "has-error" : null}
/>
{touched.phone && errors.phone ? (
<div className="error-message">{errors.phone}</div>
) : null}
</Form.Group>
</Col>
</Row>
<Row className="mb-5">
<Col lg={6} md={6} sm={12}>
<Form.Group>
<Form.Label className="form_label" >Email</Form.Label>
<Form.Control
type="text"
name="email"
placeholder="Email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
className={touched.email && errors.email ? "has-error" : null}
/>
{touched.email && errors.email ? (
<div className="error-message">{errors.email}</div>
) : null}
</Form.Group>
</Col>
<Col lg={6} md={6} sm={12}>
{/* <Form.Select aria-label="Default select example">
<option>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</Form.Select> */}
<Form.Group controlId="exampleForm.ControlSelect1">
<Form.Label>Example select</Form.Label>
<Form.Control as="select" name ="selectionOption" onChange={handleChange}
onBlur={handleBlur} value={values.selectionOption}
className={touched.selectionOption && errors.selectionOption ? "has-error" : null}
>
{
dropdown.map(drop=>{return(
<option key={drop.value} value={drop.value}>
{drop.key}
</option>
)
}
)
}
</Form.Control>
{/* <ErrorMessage name="selectionOption"></ErrorMessage> */}
{touched.selectionOption && errors.selectionOption ? (
<div className="error-message">{errors.selectionOption}</div>
) : null}
{/* <Form.Label>Example select</Form.Label>
<Field as="select" name ="selectionOption" onChange={handleChange}
onBlur={handleBlur} value={values.selectionOption}
style={{ display: "block" }}
// isValid={touched.selectionOption && !errors.selectionOption}
// isInvalid={!errors.selectionOption}
className={touched.selectionOption && errors.selectionOption ? "has-error" : null}
>
<option className='text-muted'>---</option>
<option value="ortho">ortho</option>
<option value="pedri">pedri</option>
<option value="crown">crown</option>
</Field>
{touched.selectionOption && errors.selectionOption ? (
<div className="error-message">{errors.selectionOption}</div>
) : null} */}
</Form.Group>
</Col>
</Row>
<Row className="mb-5">
<Col lg={12} md={12} sm={12}>
<Form.Group controlId="formmsg">
<Form.Label>Messages :</Form.Label>
<Form.Control
type="text"
name="msg"
as="textarea" rows={4}
placeholder="Query / Feedback"
onChange={handleChange}
onBlur={handleBlur}
value={values.msg}
className={touched.msg && errors.msg ? "has-error" : null}
/>
{touched.msg && errors.msg ? (
<div className="error-message">{errors.msg}</div>
) : null}
</Form.Group>
</Col>
</Row>
<Button type="submit" className={style.customizeBtn} name="contact" id="contact" disabled={isSubmitting}>
<Image src="img/send.png" className={style.imgBtn} />
<span className={style.titleBtn}>Submit</span>
</Button>
</Form>
)}
</Formik>
</>
)
}
export default Signup;

handleSubmit with Formik using TypeScript

I have successfully written a Login component in React using plain Javascript. I would now like to convert this component to TypeScript. I have defined some types and also threw in some "any" just to initially compile. Unfortunately, the onClick parameter in my submit button throws the following error:
TS2322: Type '(e?: FormEvent | undefined) => void' is not assignable to type '(event: MouseEvent<HTMLElement, MouseEvent>)
=> void'.
Here is the relevant code:
class Login extends React.Component<LoginProps, LoginState> {
constructor(props) {
super(props);
this.login = this.login.bind(this);
}
async login(values) {
const user = {
email: values.email,
password: values.password,
};
const query = `mutation userLogin(
$user: UserLoginInputs!
) {
userLogin(
user: $user
) {
token
}
}`;
const data: any = await graphQLFetch(query, { user });
if (data && data.userLogin.token) {
const decoded: any = jwt.decode(data.userLogin.token);
const { onUserChange } = this.props;
onUserChange({ loggedIn: true, givenName: decoded.givenName });
const { history } = this.props;
history.push('/');
}
}
render() {
return (
<Formik
onSubmit={this.login}
validationSchema={loginSchema}
initialValues={{
email: '',
password: '',
}}
>
{({
handleSubmit,
handleChange,
values,
}) => (
<Card>
<Card.Body>
<Form>
<Form.Group>
<Form.Label>E-mail</Form.Label>
<Form.Control
name="email"
value={values.email}
onChange={handleChange}
/>
</Form.Group>
<Form.Group>
<Form.Label>Password</Form.Label>
<Form.Control
name="password"
value={values.password}
onChange={handleChange}
/>
</Form.Group>
</Form>
<Button
type="button"
variant="primary"
onClick={handleSubmit}
>
Submit
</Button>
</Card.Body>
</Card>
)}
</Formik>
);
}
}
I'm new to TypeScript and don't fully understand why an error occurs regarding e versus event when the login function does not explicitly reference either of those. Can someone please help me assign types to my handleSubmit function (aka login)?
I hope that example could help you to resolve your issue
import { Field, Form, Formik } from 'formik';
import * as React from 'react';
import './style.css';
interface MyFormValues {
firstName: string;
}
export default function App() {
const initialValues: MyFormValues = { firstName: '' };
const getSubmitHandler =
() =>
(values, formik) => {
console.log({ values, formik });
alert(JSON.stringify(values, null, 2));
formik.setSubmitting(false);
};
return (
<div>
<Formik
initialValues={initialValues}
onSubmit={getSubmitHandler()}
>
{(formik) => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field id="firstName" name="firstName" placeholder="First Name" />
<button
type="button"
onClick={(event) =>
getSubmitHandler()(formik.values, formik)
}
>
Submit
</button>
</Form>
)}
</Formik>
</div>
);
}

React Bootstrap can't validate forms

I am trying to add some validation to my React + Bootstrap project but I am not able to achieve this. According to Bootstrap Form validation documentation I have to provide a Form.Control.Feedback component to the Form Group component.
But is not showing the errors but check marks like everything is fine. I created this Code Sandbox yo demostrate what I need to achieve and what is showing me:
And just in case, this is the code if you don't need the sandbox to see the error:
import React, { useState } from "react";
import "./styles.css";
import { Container, Form, Button } from "react-bootstrap";
import validator from "validator";
import empty from "is-empty";
export default function App() {
const [validated, setValidated] = useState(false);
const [errors, setErrors] = useState({});
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const handleSubmit = async e => {
e.preventDefault();
const errors = {};
// validation
if (!validator.isMobilePhone(phone, ["es-UY"])) {
errors.phone = "Invalid phone number";
}
if (!validator.isEmail(email)) {
errors.email = "Invalid email address";
}
if (!empty(errors)) {
setErrors(errors);
}
setValidated(true);
};
return (
<Container>
<Form validated={validated} onSubmit={handleSubmit}>
<h1>Test form</h1>
<Form.Group controlId="phone">
<Form.Label>Phone number</Form.Label>
<Form.Control
type="tel"
value={phone}
onChange={e => setPhone(e.target.value)}
/>
<Form.Control.Feedback type="invalid">Error</Form.Control.Feedback>
{errors.phone && (
<Form.Control.Feedback type="invalid">
{errors.phone}
</Form.Control.Feedback>
)}
</Form.Group>
<Form.Group controlId="email">
<Form.Label>Email address</Form.Label>
<Form.Control
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
feedback="Error"
/>
{errors.email && (
<Form.Control.Feedback type="invalid">
{errors.email}
</Form.Control.Feedback>
)}
</Form.Group>
<Form.Group controld="submit">
<Button type="submit" variant="primary">
Submit
</Button>
<Button
type="reset"
variant="info"
style={{ marginLeft: 10 }}
onClick={() => {
setErrors({});
setValidated(false);
}}
>
Reset
</Button>
</Form.Group>
</Form>
</Container>
);
}
So, what am I doing wrong?
Make sure you've set setValidated as false for the errors
const handleSubmit = async e => {
e.preventDefault();
const allErrors = {}; // <--- changed this as well
// validation
if (!validator.isMobilePhone(phone, ["es-UY"])) {
allErrors.phone = "Invalid phone number";
}
if (!validator.isEmail(email)) {
allErrors.email = "Invalid email address";
}
if (!empty(allErrors)) {
setErrors(allErrors);
setValidated(false);
}
else {
setValidated(true);
}
};
In your field add isInvalid as props:
<Form.Control
type="tel"
value={phone}
isInvalid={!!errors.phone} <------
onChange={e => setPhone(e.target.value)}
/>
<Form.Control
type="text"
value={email}
isInvalid={!!errors.email} <-----
onChange={e => setEmail(e.target.value)}
feedback="Error"
/>

Categories

Resources