react- setState doesn't clear textbox - reactjs

I want to clear textbox fields on success or error. how can I do that.?
On success or error, calling a function to show the snack bar. Within that, I'm updating the state. but it is not working fine.
any help appreciated.!
function
openSnackbar = ({ message }) => {
this.setState({
open: true,
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: '',
message
});
};
textfield
<TextField
id="cp_currentPassword"
label="Current Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_currentPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>;
component
import React, { Component } from 'react'
import withStyles from "#material-ui/core/styles/withStyles";
import CircularProgress from '#material-ui/core/CircularProgress';
import TextField from '#material-ui/core/TextField';
import GridItem from "../uiComponents/Grid/GridItem.jsx";
import GridContainer from "../uiComponents/Grid/GridContainer.jsx";
import Button from "../uiComponents/CustomButtons/Button.jsx";
import Card from "../uiComponents/Card/Card.jsx";
import CardHeader from "../uiComponents/Card/CardHeader.jsx";
import CardBody from "../uiComponents/Card/CardBody.jsx";
import CardFooter from "../uiComponents/Card/CardFooter.jsx";
import Snackbar from '#material-ui/core/Snackbar';
import CloseIcon from '#material-ui/icons/Close';
import { Redirect } from 'react-router-dom'
import IconButton from '#material-ui/core/IconButton';
import { connect } from 'react-redux'
import { compose } from 'redux'
import { changePassword } from '../../store/actions/auth'
const styles = {
textField: {
fontSize: '5px'
},
};
class ChangePassword extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
open: false,
message: '',
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
}
}
componentDidUpdate = (prevProps) => {
const { authError } = this.props;
console.log(authError)
if (authError != prevProps.authError) {
this.setState(
{
loading: false,
message: authError,
open: true
})
}
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
openSnackbar = ({ message }) => {
this.setState({
open: true,
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: '',
message
});
};
handleSubmit = (e) => {
e.preventDefault();
let curpass = this.state.cp_currentPassword
let newpass = this.state.cp_newPassword
this.setState({ loading: true });
this.props.changePassword(curpass, newpass, this.passwordUpdated)
}
passwordUpdated = () => {
this.setState({
message: 'Password changed Successfully.!',
open: true,
loading: false,
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
});
};
render() {
const { classes, auth, authError } = this.props;
console.log(authError)
const { loading } = this.state;
const message = (
<span
id="snackbar-message-id"
dangerouslySetInnerHTML={{ __html: this.state.message }}
/>
);
if (!auth.uid) return <Redirect to='/signin' />
return (
<div>
{/* {authError ? this.openSnackbar({ message: '{authError}' }) : null} */}
{/* {authError && !this.state.open ? this.openSnackbar({ message: '{authError}' }) : null} */}
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Card>
<CardHeader color="warning">
<h4 className={classes.cardTitleWhite}>Change Password</h4>
</CardHeader>
<form >
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CardBody>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_currentPassword"
label="Current Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_currentPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_newPassword"
label="New Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_newPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_confirmPassword"
label="Confirm Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_confirmPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
</GridContainer>
</CardBody>
<CardFooter>
<Button color="warning" onClick={(e) => this.handleSubmit(e)} disabled={loading}>
{loading && <CircularProgress style={{ color: 'white', height: '20px', width: '20px', marginRight: '10px' }} />}
Change Password
</Button>
</CardFooter>
</GridItem>
</GridContainer>
</form>
</Card>
</GridItem>
</GridContainer>
<Snackbar
open={this.state.open}//{!!this.props.authError}//
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
message={message}
variant="error"
onClose={() => this.setState({ open: false, message: '' })}
action={
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() =>
// clearAuthError
this.setState({ open: false, message: '' })
}
>
<CloseIcon className={classes.icon} />
</IconButton>
}
autoHideDuration={3000}
/>
</div>
)
}
}
const mapstateToProps = (state) => {
return {
auth: state.firebase.auth,
authError: state.authroot.autherr
}
}
const mapDispatchtoProps = (dispatch, getState) => {
return {
changePassword: (currentPassword, newPassword, passwordUpdated) => { dispatch(changePassword(currentPassword, newPassword, passwordUpdated)) },
}
}
export default compose(
withStyles(styles),
connect(mapstateToProps, mapDispatchtoProps)
)(ChangePassword);

In your handleSubmit function don't pass this.passwordUpdated
handleSubmit = (e) => {
e.preventDefault();
let curpass = this.state.cp_currentPassword
let newpass = this.state.cp_newPassword
this.setState({ loading: true });
this.props.changePassword(curpass, newpass, this.passwordUpdated) //Remove this.passwordUpdated from here
}
Instead of ComponentDidUpdate method you should use ComponentWillReceiveProps
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps', nextProps);
if (this.props.authError !== nextProps.authError) {
// here you can make your textbox empty
this.setState({
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
});
}
}
Note: In new version of react you must use static getDerivedStateFromProps() instead of componentWillReceiveProps.

Related

Required TextField validation on Material-UI

If something is written on the required TextField then everything will be okay but if the required TextField is blank then it will show me an error message on a helperText. But the problem is I can't use lenth on a if condition where I have set an onClick handler or method but I can use lenth on an onChange handler or method. If I can't use lenth then how will I know that something is written on required TextField. Here is my code:
import React, { Component } from 'react';
import { Container, Grid, Typography, TextField, Button } from '#material-ui/core';
import { withStyles } from "#material-ui/core/styles";
const styles = theme => ({
mT: {
marginTop: theme.spacing(1)
},
mR: {
marginRight: theme.spacing(3)
}
});
class Account extends Component {
constructor(){
super()
this.state={
accname: '',
helperText: '',
error: false
}
this.onChange = this.onChange.bind(this);
this.onClick = this.onClick.bind(this);
}
onChange = (event) => {
this.setState({[event.target.name]:event.target.value})
}
onClick = (event) => {
if (event.target.required > 0) {
this.setState({ helperText: '', error: false });
} else {
this.setState({ helperText: 'Invalid format', error: true });
}
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<Container>
<Grid container
spacing={2}>
<Grid item
xs={12}
sm={12}
md={12}
lg={12}
xl={12}>
<Typography className={classes.mT}
variant="h4"
color="textPrimary">
Add Account
</Typography>
</Grid>
<Grid item
xs={12}
sm={12}
md={12}
lg={12}
xl={12}>
<TextField className={classes.mR}
label="Account Name"
name="accname"
type="text"
helperText={this.state.helperText}
onChange={this.onChange}
value={this.state.accname}
error={this.state.error}
variant="outlined"
color="primary"
size="small"
autoFocus
required />
<Button variant="contained"
color="primary"
onClick={this.onClick} >
Submit
</Button>
</Grid>
</Grid>
</Container>
</React.Fragment>
);
}
}
export default withStyles(styles, { withTheme: true })(Account);
onClick = () => {
if(
this.state.accname.trim() != '' &&
this.state.firstName.trim() != '' &&
this.state.lastName.trim() != ''
){
//Your API call here
}
else{
alert("fill the empty spaces!");
}

Failed prop type: Material-UI: Either `children`, `image`, `src` or `component` prop must be specified in ForwardRef(CardMedia)

Whenever I'm trying to post new Screams in my page. I'm not getting the image, title and content of the card. I need to reload to get that.
It is saying failed prop types means after posting a scream I'm not getting the value from prop types but after a reload, everything seems to be fine. I don't know what is wrong is going. Please have a look at my code.
This is my Screams.js from where i'm showing my Screams
Scream.js
const styles = {
card: {
position: "relative",
display: "flex",
marginBottom: 20,
},
image: {
minWidth: 150,
},
content: {
padding: 25,
objectFit: "cover",
},
};
class Scream extends Component {
render() {
dayjs.extend(relativeTime);
const {
classes,
scream: {
body,
createdAt,
userImage,
userHandle,
screamId,
likeCount,
commentCount,
},
user: {
authenticated,
credentials: { handle },
},
} = this.props;
const deleteButton =
authenticated && userHandle === handle ? (
<DeleteScream screamId={screamId} />
) : null;
const likeButton = !authenticated ? (
<Link to="/login">
<MyButton tip="Like">
<FavoriteBorder color="primary" />
</MyButton>
</Link>
) : this.likedScream() ? (
<MyButton tip="Undo like" onClick={this.unlikeScream}>
<FavoriteIcon color="primary" />
</MyButton>
) : (
<MyButton tip="Like" onClick={this.likeScream}>
<FavoriteBorder color="primary" />
</MyButton>
);
return (
<Card className={classes.card}>
<CardMedia
image={userImage}
title="Profile Image"
className={classes.image}
/>
<CardContent className={classes.content}>
<Typography variant="h5" component={Link} to={`/users/${userHandle}`}>
{userHandle}
</Typography>
{deleteButton}
<Typography variant="body2" color="textSecondary">
{dayjs(createdAt).fromNow()}
</Typography>
<Typography variant="body1">{body}</Typography>
{likeButton}
<span> {likeCount} Likes </span>
<MyButton tip="comments">
<ChatIcon color="primary" />
</MyButton>
<span> {commentCount} Comments </span>
</CardContent>
</Card>
);
}
}
Scream.propTypes = {
likeScream: PropTypes.func.isRequired,
unlikeScream: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
scream: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
};
const mapStateToprops = (state) => ({
user: state.user,
});
const mapActionToProps = {
likeScream,
unlikeScream,
};
export default connect(
mapStateToprops,
mapActionToProps
)(withStyles(styles)(Scream));
As u can see i have userImage, userHandle and body of the card in the props and it is showing it on my page.
But after posting a new Scream, i'm not getting the image, userHandle and body of the new Scream unless and until reload it.
PostScream.js
class PostScream extends Component {
state = {
open: false,
body: "",
errors: {}
}
componentWillReceiveProps(nextProps) {
if(nextProps.UI.errors) {
this.setState({
errors: nextProps.UI.errors
});
}
if(!nextProps.UI.errors && !nextProps.UI.loading) {
this.setState({
body: "",
open: false,
errors: {}
})
}
}
handleOpen = () => {
this.setState({ open: true })
};
handleClose = () => {
this.props.clearErrors();
this.setState({ open: false, errors: {} });
};
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value })
}
handleSubmit = (event) => {
event.preventDefault();
this.props.postScream({ body: this.state.body })
}
render() {
const { errors } = this.state;
const { classes, UI: { loading }} = this.props;
return (
<Fragment>
<MyButton onClick= { this.handleOpen } tip="Post a Scream!">
<AddIcon />
</MyButton>
<Dialog
open= { this.state.open }
onClose= { this.handleClose }
fullWidth
maxWidth = "sm"
>
<MyButton
tip="Close"
onClick={this.handleClose}
tipClassName={classes.closeButton}
>
<CloseIcon />
</MyButton>
<DialogTitle> Post a new Scream </DialogTitle>
<DialogContent>
<form onSubmit= { this.handleSubmit }>
<TextField
name="body"
type="text"
label="SCREAM!"
multiline
rows="3"
placeholder= "What's on your mind!"
error={ errors.body ? true: false }
helperText={ errors.body }
className={classes.TextField}
onChange={ this.handleChange }
fullWidth
/>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.submitButton}
disabled={ loading }
>
Submit
{loading && (
<CircularProgress size={30} className={ classes.progressSpinner } />
)}
</Button>
</form>
</DialogContent>
</Dialog>
</Fragment>
)
}
}
PostScream.propTypes = {
postScream: PropTypes.func.isRequired,
clearErrors: PropTypes.func.isRequired,
UI: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
UI: state.UI
});
export default connect(
mapStateToProps,
{ postScream, clearErrors }
)(withStyles(styles)(PostScream));
After posting a new Scream, i am getting Scream like this:-
I was having the same issue, I was able to fix it by adding component='img' to my CardMedia.
<CardMedia
image={userImage}
title="Profile Image"
className={classes.image}
component='img'
/>
Specifying the component as an img "Hides" the images since CardMedia is a div and your css is applied to the div. Add component = "div" to CardMedia.
<CardMedia
image={userImage}
title="Profile Image"
className={classes.image}
component='div'
/>

React getting Maximum update depth exceeded error

I'm performing a change password, for authError I'm getting the following error..
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
changepassword.js
import React, { Component } from 'react'
import withStyles from "#material-ui/core/styles/withStyles";
import { Redirect } from 'react-router-dom'
import IconButton from '#material-ui/core/IconButton';
import { connect } from 'react-redux'
import { compose } from 'redux'
import {changePassword } from '../../store/actions/auth'
const styles = {
textField: {
fontSize: '5px'
},
};
class ChangePassword extends Component {
state = {
loading: false,
open:false,
message:'',
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
}
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
openSnackbar = ({ message }) => {
this.setState({
open: true,
message,
});
};
handleSubmit = (e) => {
e.preventDefault();
let curpass=this.state.cp_currentPassword
let newpass=this.state.cp_newPassword
this.setState({loading:true});
this.props.changePassword(curpass,newpass)
this.openSnackbar({ message: 'Password changed Successfully.!' })
}
render() {
const { classes, auth, authError } = this.props;
const { loading } = this.state;
const message = (
<span
id="snackbar-message-id"
dangerouslySetInnerHTML={{ __html: this.state.message }}
/>
);
if (!auth.uid) return <Redirect to='/signin' />
return (
<div>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Card>
<CardHeader color="warning">
<h4 className={classes.cardTitleWhite}>Change Password</h4>
</CardHeader>
<form >
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CardBody>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_currentPassword"
label="Current Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_currentPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_newPassword"
label="New Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_newPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_confirmPassword"
label="Confirm Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_confirmPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
</GridContainer>
</CardBody>
<CardFooter>
<Button color="warning" onClick={this.handleSubmit} disabled={loading}>
{loading && <CircularProgress style={{ color: 'white', height: '20px', width: '20px', marginRight: '10px' }} />}
Change Password
</Button>
</CardFooter>
</GridItem>
</GridContainer>
</form>
</Card>
</GridItem>
</GridContainer>
{authError ? this.openSnackbar({ message: '{authError}' }) : null}
<Snackbar
open={this.state.open}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
message={message}
variant="error"
onClose={() => this.setState({ open: false, message: '' })}
action={
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => this.setState({ open: false, message: '' })}
>
<CloseIcon className={classes.icon} />
</IconButton>
}
autoHideDuration={3000}
/>
</div>
)
}
}
const mapstateToProps = (state) => {
return {
auth: state.firebase.auth,
authError: state.authroot.autherr
}
}
const mapDispatchtoProps = (dispatch) => {
return {
changePassword: (currentPassword,newPassword) => { dispatch(changePassword(currentPassword,newPassword)) }
}
}
export default compose(
withStyles(styles),
connect(mapstateToProps,mapDispatchtoProps)
)(ChangePassword);
change password action
export const changePassword = (currentPassword, newPassword) => {
return (dispatch, getState, { getFirebase }) => {
const firebase = getFirebase();
console.log(currentPassword);
console.log(newPassword);
var user = firebase.auth().currentUser;
user.updatePassword(newPassword).then(() => {
console.log("Password updated!");
}).catch((error) => {
dispatch({ type: 'CHANGEPASSWORD_ERR', error })});
}
}
You are updating state here
{authError ? this.openSnackbar({ message: '{authError}' }) : null
that line runs multiple times, as it checks if there was auth error, if there was call openSnackBar, openSnackBar update state, which cause the component to re-render, after re-render the check happens again etc, which causes a loop. change it to the following and only call openSnackBar when state.open is false.
{authError && !this.state.open ? this.openSnackbar({ message: '{authError}' }) : null}
EDIT
remove authError from render and check in componentDidMount
componentDidMount = () => {
const { authError } = this.props;
if (authError) {
this.openSnackbar({ message: '{authError}' });
}
};

Can't get jest test to fire button onclick

I am completely new to both react and testing and am a bit stumped.
I was just wondering if someone could tell me why my test fails. I assume I making a basic mistake in how this should work.
I am trying to test a log in page. At the moment I am just trying to get my test to fire an onclick from a button and check that that a function has been called.
The log in component code can be seen below.
import React, { Component, Fragment } from "react";
import { Redirect } from "react-router-dom";
// Resources
//import logo from "assets/img/white-logo.png";
//import "./Login.css";
// Material UI
import {
withStyles,
MuiThemeProvider,
createMuiTheme
} from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
import Person from "#material-ui/icons/Person";
import InputAdornment from "#material-ui/core/InputAdornment";
// Custom Components
import Loading from "components/Loading/Loading.jsx";
// bootstrat 1.0
import { Alert, Row } from "react-bootstrap";
// MUI Icons
import LockOpen from "#material-ui/icons/LockOpen";
// remove
import axios from "axios";
// API
import api2 from "../../helpers/api2";
const styles = theme => ({
icon: {
color: "#fff"
},
cssUnderline: {
color: "#fff",
borderBottom: "#fff",
borderBottomColor: "#fff",
"&:after": {
borderBottomColor: "#fff",
borderBottom: "#fff"
},
"&:before": {
borderBottomColor: "#fff",
borderBottom: "#fff"
}
}
});
const theme = createMuiTheme({
palette: {
primary: { main: "#fff" }
}
});
class Login extends Component {
constructor(props, context) {
super(props, context);
this.state = {
username: "",
password: "",
isAuthenticated: false,
error: false,
toggle: true,
// loading
loading: false
};
}
openLoading = () => {
this.setState({ loading: true });
};
stopLoading = () => {
this.setState({ loading: false });
};
toggleMode = () => {
this.setState({ toggle: !this.state.toggle });
};
handleReset = e => {
const { username } = this.state;
this.openLoading();
api2
.post("auth/admin/forgotPassword", { email: username })
.then(resp => {
this.stopLoading();
console.log(resp);
})
.catch(error => {
this.stopLoading();
console.error(error);
});
};
handleSubmit = event => {
event.preventDefault();
localStorage.clear();
const cred = {
username: this.state.username,
password: this.state.password
};
api2
.post("auth/admin", cred)
.then(resp => {
console.log(resp);
localStorage.setItem("api_key", resp.data.api_key);
localStorage.setItem("username", cred.username);
return this.setState({ isAuthenticated: true });
})
.catch(error => {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
console.log(error.request);
} else {
console.log("Error", error.message);
}
console.log(error.config);
return this.setState({ error: true });
});
};
handleInputChange = event => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
forgotPassword = () => {
console.log("object");
};
render() {
const { error } = this.state;
const { isAuthenticated } = this.state;
const { classes } = this.props;
if (isAuthenticated) {
return <Redirect to="/home/dashboard" />;
}
return (
<div className="login-page">
<video autoPlay muted loop id="myVideo">
<source
src=""
type="video/mp4"
/>
</video>
<div className="videoOver" />
<div className="midl">
<Row className="d-flex justify-content-center">
<img src={''} className="Login-logo" alt="logo" />
</Row>
<br />
<Row className="d-flex justify-content-center">
{error && (
<Alert style={{ color: "#fff" }}>
The username/password entered is incorrect. Try again!
</Alert>
)}
</Row>
<MuiThemeProvider theme={theme}>
<Row className="d-flex justify-content-center">
<TextField
id="input-username"
name="username"
type="text"
label="username"
value={this.state.username}
onChange={this.handleInputChange}
InputProps={{
className: classes.icon,
startAdornment: (
<InputAdornment position="start">
<Person className={classes.icon} />
</InputAdornment>
)
}}
/>
</Row>
{this.state.toggle ? (
<Fragment>
<br />
<Row className="d-flex justify-content-center">
<TextField
id="input-password"
name="password"
type="password"
label="pasword"
value={this.state.password}
onChange={this.handleInputChange}
className={classes.cssUnderline}
InputProps={{
className: classes.icon,
startAdornment: (
<InputAdornment position="start">
<LockOpen className={classes.icon} />
</InputAdornment>
)
}}
/>
</Row>
</Fragment>
) : (
""
)}
</MuiThemeProvider>
<br />
<Row className="d-flex justify-content-center">
{this.state.toggle ? (
<Button
className="button login-button"
data-testid='submit'
type="submit"
variant="contained"
color="primary"
onClick={this.handleSubmit}
name = "logIn"
>
Login
</Button>
) : (
<Button
className="button login-button"
type="submit"
variant="contained"
color="primary"
onClick={this.handleReset}
>
Reset
</Button>
)}
</Row>
<Row className="d-flex justify-content-center">
<p onClick={this.toggleMode} className="text-link">
{this.state.toggle ? "Forgot password?" : "Login"}
</p>
</Row>
</div>
<Loading open={this.state.loading} onClose={this.handleClose} />
</div>
);
}
}
export default withStyles(styles)(Login);
My current test which fails.
import React from 'react'
import {render, fireEvent, getByTestId} from 'react-testing-library'
import Login from './login'
describe('<MyComponent />', () => {
it('Function should be called once', () => {
const functionCalled = jest.fn()
const {getByTestId} = render(<Login handleSubmit={functionCalled} />)
const button = getByTestId('submit');
fireEvent.click(button);
expect(functionCalled).toHaveBeenCalledTimes(1)
});
});
I'm also fairly new to React Testing Library, but it looks like your button is using this.handleSubmit, so passing your mock function as a prop won't do anything. If you were to pass handleSubmit in from some container, then I believe your tests, as you currently have them, would pass.

Error message "shap is not a function" - what shall I do?

I get an error message I didn't figure out how to get rid off:
yup__WEBPACK_IMPORTED_MODULE_12__.object(...).shap is not a function
import React from 'react'
import Button from '#material-ui/core/Button'
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 { Link } from 'gatsby'
import DisplayOutput from '../pages/DisplayOutput'
import { Formik, Field, Form, ErrorMessage } from 'formik'
import * as Yup from 'yup'
class DialogFormWithFormik extends React.Component {
constructor(props) {
super(props)
this.state = {
open: false,
dialogModal: 'none',
}
}
handleClickOpen = () => {
this.setState({ open: true })
this.setState({ dialogModal: 'login' })
}
handleRegisterClickOpen = () => {
this.setState({ open: true })
this.setState({ dialogModal: 'register' })
}
handleClose = () => {
this.setState({ dialogModal: false })
}
onSubmit = values => {
console.log(values)
alert('values submitted')
}
form = props => {
return (
<div>
<Button
variant="outlined"
color="primary"
onClick={() => this.handleClickOpen()}
>
Login
</Button>
<Button
variant="outlined"
color="primary"
onClick={() => this.handleRegisterClickOpen()}
>
Register
</Button>
<Dialog
onClose={() => this.handleClose()}
aria-labelledby="customized-dialog-title"
open={this.state.dialogModal === 'login'}
>
<DialogTitle id="form-dialog-title">
To Display Student Data
</DialogTitle>
<DialogContent />
<form onSubmit={props.handleSubmit}>
<TextField
label="Username"
type="text"
margin="normal"
name="userName"
/>
<ErrorMessage name="userName" />
<br />
<TextField
label="Password"
type="password"
autoComplete="current-password"
margin="normal"
/>
<ErrorMessage name="password" />
<DialogActions>
<nav>
<Button color="primary">Login</Button>
</nav>
<br />
<Button onClick={() => this.handleClose()}>Cancel</Button>
</DialogActions>
</form>
</Dialog>
</div>
)
}
schema = () => {
const schema = Yup.object().shap({
userName: Yup.string().required(),
password: Yup.string().required(),
})
return schema
}
render() {
return (
<div align="center">
<Formik
initialValues={{
userName: '',
password: '',
}}
onSubmit={this.onSubmit}
render={this.form}
validationSchema={this.schema()}
/>
</div>
)
}
}
export default DialogFormWithFormik

Resources