From fa133c40edb633c63d37619682ba0771d4481ed9 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 02:24:00 +0300 Subject: fix input validation errors --- src/containers/Registration/Registration.tsx | 103 ++++++++++++++++----------- 1 file changed, 63 insertions(+), 40 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 9bcea8e..7d2a758 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -1,10 +1,13 @@ import React, { useState, useRef } from 'react'; import { useHistory } from 'react-router-dom'; import { makeStyles } from '@material-ui/core/styles'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import CheckCircleIcon from '@material-ui/icons/CheckCircle'; -import InputAdornment from '@material-ui/core/InputAdornment'; +import { + TextField, + Button, + InputAdornment, + IconButton +} from '@material-ui/core'; +import { CheckCircle, Visibility, VisibilityOff } from '@material-ui/icons'; import { post } from '../../requests'; import { useAuth } from '../../hooks/useAuth'; @@ -35,18 +38,20 @@ const useStyles = makeStyles(theme => ({ } })); -const inputStyle = { WebkitBoxShadow: '0 0 0 1000px snow inset' }; - +interface ValidationStates { + validUsername: boolean | undefined; + validEmail: boolean | undefined; + validPassword: boolean | undefined; + showPassword: boolean; +} const Registration: React.FC = () => { - const errorOutputs = { - usernameError: 'Username is required', - emailError: 'Invalid email address', - passwordError: 'Should be at least 6 characters' - }; - const [errorPassword, setErrorPassword] = useState<boolean>(false); - const [errorEmail, setErrorEmail] = useState<boolean>(false); - const [errorUsername, setErrorUsername] = useState<boolean>(false); + const [values, setValues] = useState<ValidationStates>({ + validUsername: undefined, + validEmail: undefined, + validPassword: undefined, + showPassword: false + }); const classes = useStyles(); const usernameRef = useRef<HTMLInputElement>(); @@ -55,11 +60,15 @@ const Registration: React.FC = () => { const { login } = useAuth(); const history = useHistory(); + const checkFromValidation = () => { + return values.validUsername && values.validEmail && values.validPassword; + }; + const handleSubmit = () => { const username = usernameRef.current?.value?.toLowerCase(); const password = passwordRef.current?.value; const email = emailRef.current?.value; - if (username && password) { + if (username && password && checkFromValidation()) { post('/users', { username, password, email }) .then(() => login(username, password)) .then(() => history.push(`/profile/${username}`)); @@ -70,14 +79,20 @@ const Registration: React.FC = () => { history.push('/login'); }; - const handleLoginChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setErrorUsername(e.currentTarget.value.length === 0); + const handleClickShowPassword = () => { + setValues({ ...values, showPassword: !values.showPassword }); + }; + const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => { + event.preventDefault(); + }; + const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => { + setValues({ ...values, validUsername: e.currentTarget.value.length > 0 }); }; const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setErrorEmail(!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value)); + setValues({ ...values, validEmail: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) }); }; const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setErrorPassword(e.currentTarget.value.length < 6); + setValues({ ...values, validPassword: e.currentTarget.value.length > 6 }); }; return ( @@ -87,46 +102,54 @@ const Registration: React.FC = () => { <TextField inputRef={usernameRef} label="Username" - error={errorUsername} - helperText={errorUsername && errorOutputs.usernameError} + error={!values.validUsername} + helperText={!values.validUsername && 'This field is required'} required - onChange={handleLoginChange} - inputProps={{ style: inputStyle }} + onChange={handleUsernameChange} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + {values.validUsername && values.validUsername !== undefined && <CheckCircle color="primary" />} + </InputAdornment> + ) + }} /> <TextField inputRef={emailRef} label="Email" - error={errorEmail} - helperText={errorEmail && errorOutputs.emailError} + error={!values.validEmail} + helperText={!values.validEmail && 'Invalid email address'} onChange={handleEmailChange} - InputProps={errorEmail ? {} : { + InputProps={{ endAdornment: ( <InputAdornment position="end"> - <CheckCircleIcon color="primary" /> + {values.validEmail && values.validEmail !== undefined && <CheckCircle color="primary" />} </InputAdornment> - ), - inputProps: { - style: inputStyle - } + ) }} /> <TextField inputRef={passwordRef} label="Password" - type="password" required - error={errorPassword} - helperText={errorPassword && errorOutputs.passwordError} + error={!values.validPassword} + helperText={!values.validPassword && 'Should be at least 6 characters'} + type={values.showPassword ? 'text' : 'password'} onChange={handlePasswordChange} - InputProps={errorPassword ? {} : { + InputProps={{ endAdornment: ( <InputAdornment position="end"> - <CheckCircleIcon color="primary" /> + <IconButton + size="small" + aria-label="toggle password visibility" + onClick={handleClickShowPassword} + onMouseDown={handleMouseDownPassword} + > + {values.showPassword ? <Visibility /> : <VisibilityOff />} + </IconButton> + {values.validPassword && values.validPassword !== undefined && <CheckCircle color="primary" />} </InputAdornment> - ), - inputProps: { - style: inputStyle - } + ) }} /> <Button variant="contained" onClick={handleSubmit}>submit</Button> -- cgit v1.2.3 From 4bbebc183e75e287e28d5b4369699d1bc40c0cd1 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 18:40:23 +0300 Subject: feat: submit Form on button click --- src/containers/Registration/Registration.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 7d2a758..20bc283 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef } from 'react'; +import React, {useState, useRef, FormEvent} from 'react'; import { useHistory } from 'react-router-dom'; import { makeStyles } from '@material-ui/core/styles'; import { @@ -64,11 +64,13 @@ const Registration: React.FC = () => { return values.validUsername && values.validEmail && values.validPassword; }; - const handleSubmit = () => { + const handleSubmit = (event: FormEvent<HTMLFormElement>) => { + event.preventDefault(); const username = usernameRef.current?.value?.toLowerCase(); const password = passwordRef.current?.value; const email = emailRef.current?.value; if (username && password && checkFromValidation()) { + console.log('yes'); post('/users', { username, password, email }) .then(() => login(username, password)) .then(() => history.push(`/profile/${username}`)); @@ -98,7 +100,7 @@ const Registration: React.FC = () => { return ( <> <div className={classes.formHeader}>Sign Up</div> - <form className={classes.root} noValidate autoComplete="off"> + <form className={classes.root} noValidate autoComplete="off" onSubmit={handleSubmit}> <TextField inputRef={usernameRef} label="Username" @@ -152,7 +154,7 @@ const Registration: React.FC = () => { ) }} /> - <Button variant="contained" onClick={handleSubmit}>submit</Button> + <Button variant="contained" type="submit" >submit</Button> </form> <div className={classes.formTransfer}> <div>Already have an account?</div> -- cgit v1.2.3 From b7be53c172869679cdfa65a44591c7bd9c5b9302 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 18:46:47 +0300 Subject: separate validation state into 2 states and remove CheckCircle icon --- src/containers/Registration/Registration.tsx | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 20bc283..0746deb 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -38,20 +38,19 @@ const useStyles = makeStyles(theme => ({ } })); -interface ValidationStates { +interface errors { validUsername: boolean | undefined; validEmail: boolean | undefined; validPassword: boolean | undefined; - showPassword: boolean; } const Registration: React.FC = () => { - const [values, setValues] = useState<ValidationStates>({ + const [values, setValues] = useState<errors>({ validUsername: undefined, validEmail: undefined, validPassword: undefined, - showPassword: false }); + const [showPassword, setShowPassword] = useState<boolean>(false); const classes = useStyles(); const usernameRef = useRef<HTMLInputElement>(); @@ -82,7 +81,7 @@ const Registration: React.FC = () => { }; const handleClickShowPassword = () => { - setValues({ ...values, showPassword: !values.showPassword }); + setShowPassword(prevState => !prevState); }; const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => { event.preventDefault(); @@ -108,13 +107,6 @@ const Registration: React.FC = () => { helperText={!values.validUsername && 'This field is required'} required onChange={handleUsernameChange} - InputProps={{ - endAdornment: ( - <InputAdornment position="end"> - {values.validUsername && values.validUsername !== undefined && <CheckCircle color="primary" />} - </InputAdornment> - ) - }} /> <TextField inputRef={emailRef} @@ -122,13 +114,6 @@ const Registration: React.FC = () => { error={!values.validEmail} helperText={!values.validEmail && 'Invalid email address'} onChange={handleEmailChange} - InputProps={{ - endAdornment: ( - <InputAdornment position="end"> - {values.validEmail && values.validEmail !== undefined && <CheckCircle color="primary" />} - </InputAdornment> - ) - }} /> <TextField inputRef={passwordRef} @@ -136,7 +121,7 @@ const Registration: React.FC = () => { required error={!values.validPassword} helperText={!values.validPassword && 'Should be at least 6 characters'} - type={values.showPassword ? 'text' : 'password'} + type={showPassword ? 'text' : 'password'} onChange={handlePasswordChange} InputProps={{ endAdornment: ( @@ -147,7 +132,7 @@ const Registration: React.FC = () => { onClick={handleClickShowPassword} onMouseDown={handleMouseDownPassword} > - {values.showPassword ? <Visibility /> : <VisibilityOff />} + {showPassword ? <Visibility /> : <VisibilityOff />} </IconButton> {values.validPassword && values.validPassword !== undefined && <CheckCircle color="primary" />} </InputAdornment> -- cgit v1.2.3 From 9c571fa6e99d3c5e271c5763d86bf4cdd702aebb Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 18:47:40 +0300 Subject: change state name --- src/containers/Registration/Registration.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 0746deb..47b5947 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -38,14 +38,14 @@ const useStyles = makeStyles(theme => ({ } })); -interface errors { +interface ErrorStates { validUsername: boolean | undefined; validEmail: boolean | undefined; validPassword: boolean | undefined; } const Registration: React.FC = () => { - const [values, setValues] = useState<errors>({ + const [values, setValues] = useState<ErrorStates>({ validUsername: undefined, validEmail: undefined, validPassword: undefined, -- cgit v1.2.3 From da1854e8fd34245e2e9e4fd941320f08b1cc40e1 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 18:55:34 +0300 Subject: add useMemo for check validity of the form --- src/containers/Registration/Registration.tsx | 41 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 47b5947..3269f6e 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -1,4 +1,4 @@ -import React, {useState, useRef, FormEvent} from 'react'; +import React, {useState, useRef, FormEvent, useMemo} from 'react'; import { useHistory } from 'react-router-dom'; import { makeStyles } from '@material-ui/core/styles'; import { @@ -39,16 +39,16 @@ const useStyles = makeStyles(theme => ({ })); interface ErrorStates { - validUsername: boolean | undefined; - validEmail: boolean | undefined; - validPassword: boolean | undefined; + username: boolean | undefined; + email: boolean | undefined; + password: boolean | undefined; } const Registration: React.FC = () => { const [values, setValues] = useState<ErrorStates>({ - validUsername: undefined, - validEmail: undefined, - validPassword: undefined, + username: undefined, + email: undefined, + password: undefined, }); const [showPassword, setShowPassword] = useState<boolean>(false); @@ -59,16 +59,16 @@ const Registration: React.FC = () => { const { login } = useAuth(); const history = useHistory(); - const checkFromValidation = () => { - return values.validUsername && values.validEmail && values.validPassword; - }; + const isValid = useMemo(() => { + return values.username && values.email && values.password; + },[values]); const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); const username = usernameRef.current?.value?.toLowerCase(); const password = passwordRef.current?.value; const email = emailRef.current?.value; - if (username && password && checkFromValidation()) { + if (username && password && isValid) { console.log('yes'); post('/users', { username, password, email }) .then(() => login(username, password)) @@ -87,13 +87,13 @@ const Registration: React.FC = () => { event.preventDefault(); }; const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValues({ ...values, validUsername: e.currentTarget.value.length > 0 }); + setValues({ ...values, username: e.currentTarget.value.length > 0 }); }; const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValues({ ...values, validEmail: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) }); + setValues({ ...values, email: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) }); }; const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValues({ ...values, validPassword: e.currentTarget.value.length > 6 }); + setValues({ ...values, password: e.currentTarget.value.length > 6 }); }; return ( @@ -103,24 +103,24 @@ const Registration: React.FC = () => { <TextField inputRef={usernameRef} label="Username" - error={!values.validUsername} - helperText={!values.validUsername && 'This field is required'} + error={!values.username} + helperText={!values.username && 'This field is required'} required onChange={handleUsernameChange} /> <TextField inputRef={emailRef} label="Email" - error={!values.validEmail} - helperText={!values.validEmail && 'Invalid email address'} + error={!values.email} + helperText={!values.email && 'Invalid email address'} onChange={handleEmailChange} /> <TextField inputRef={passwordRef} label="Password" required - error={!values.validPassword} - helperText={!values.validPassword && 'Should be at least 6 characters'} + error={!values.password} + helperText={!values.password && 'Should be at least 6 characters'} type={showPassword ? 'text' : 'password'} onChange={handlePasswordChange} InputProps={{ @@ -134,7 +134,6 @@ const Registration: React.FC = () => { > {showPassword ? <Visibility /> : <VisibilityOff />} </IconButton> - {values.validPassword && values.validPassword !== undefined && <CheckCircle color="primary" />} </InputAdornment> ) }} -- cgit v1.2.3 From 987c4ecc67dc3cd41d0db04727c942efe35d5c82 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 19:21:11 +0300 Subject: change onChange to onBlur event --- src/containers/Registration/Registration.tsx | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 3269f6e..e250397 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -79,21 +79,29 @@ const Registration: React.FC = () => { const handleLogin = () => { history.push('/login'); }; - const handleClickShowPassword = () => { setShowPassword(prevState => !prevState); }; const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => { event.preventDefault(); }; - const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValues({ ...values, username: e.currentTarget.value.length > 0 }); + const handleUsernameChange = (e: React.FocusEvent<HTMLInputElement>) => { + setValues({ + ...values, + username: e.currentTarget.value.length > 0 + }); }; - const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValues({ ...values, email: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) }); + const handleEmailChange = (e: React.FocusEvent<HTMLInputElement>) => { + setValues({ + ...values, + email: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) + }); }; - const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => { - setValues({ ...values, password: e.currentTarget.value.length > 6 }); + const handlePasswordChange = (e: React.FocusEvent<HTMLInputElement>) => { + setValues({ + ...values, + password: e.currentTarget.value.length > 6 + }); }; return ( @@ -106,14 +114,14 @@ const Registration: React.FC = () => { error={!values.username} helperText={!values.username && 'This field is required'} required - onChange={handleUsernameChange} + onBlur={handleUsernameChange} /> <TextField inputRef={emailRef} label="Email" error={!values.email} helperText={!values.email && 'Invalid email address'} - onChange={handleEmailChange} + onBlur={handleEmailChange} /> <TextField inputRef={passwordRef} @@ -122,7 +130,7 @@ const Registration: React.FC = () => { error={!values.password} helperText={!values.password && 'Should be at least 6 characters'} type={showPassword ? 'text' : 'password'} - onChange={handlePasswordChange} + onBlur={handlePasswordChange} InputProps={{ endAdornment: ( <InputAdornment position="end"> -- cgit v1.2.3 From be765bb98e19fdc755418cdf4d2eb19d0aa15ecd Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 19:41:42 +0300 Subject: fix eslint errors --- src/containers/Registration/Registration.tsx | 42 +++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index e250397..955d900 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -1,4 +1,9 @@ -import React, {useState, useRef, FormEvent, useMemo} from 'react'; +import React, { + useState, + useRef, + FormEvent, + useMemo +} from 'react'; import { useHistory } from 'react-router-dom'; import { makeStyles } from '@material-ui/core/styles'; import { @@ -7,7 +12,7 @@ import { InputAdornment, IconButton } from '@material-ui/core'; -import { CheckCircle, Visibility, VisibilityOff } from '@material-ui/icons'; +import { Visibility, VisibilityOff } from '@material-ui/icons'; import { post } from '../../requests'; import { useAuth } from '../../hooks/useAuth'; @@ -46,9 +51,9 @@ interface ErrorStates { const Registration: React.FC = () => { const [values, setValues] = useState<ErrorStates>({ - username: undefined, - email: undefined, - password: undefined, + username: false, + email: false, + password: false }); const [showPassword, setShowPassword] = useState<boolean>(false); @@ -60,16 +65,15 @@ const Registration: React.FC = () => { const history = useHistory(); const isValid = useMemo(() => { - return values.username && values.email && values.password; - },[values]); + return !values.username && !values.email && !values.password; + }, [values]); - const handleSubmit = (event: FormEvent<HTMLFormElement>) => { + const handleSubmit = (event:FormEvent<HTMLFormElement>) => { event.preventDefault(); const username = usernameRef.current?.value?.toLowerCase(); const password = passwordRef.current?.value; const email = emailRef.current?.value; if (username && password && isValid) { - console.log('yes'); post('/users', { username, password, email }) .then(() => login(username, password)) .then(() => history.push(`/profile/${username}`)); @@ -88,19 +92,19 @@ const Registration: React.FC = () => { const handleUsernameChange = (e: React.FocusEvent<HTMLInputElement>) => { setValues({ ...values, - username: e.currentTarget.value.length > 0 + username: e.currentTarget.value.length === 0 }); }; const handleEmailChange = (e: React.FocusEvent<HTMLInputElement>) => { setValues({ ...values, - email: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) + email: !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) }); }; const handlePasswordChange = (e: React.FocusEvent<HTMLInputElement>) => { setValues({ ...values, - password: e.currentTarget.value.length > 6 + password: e.currentTarget.value.length < 6 }); }; @@ -111,24 +115,24 @@ const Registration: React.FC = () => { <TextField inputRef={usernameRef} label="Username" - error={!values.username} - helperText={!values.username && 'This field is required'} + error={values.username} + helperText={values.username && 'This field is required'} required onBlur={handleUsernameChange} /> <TextField inputRef={emailRef} label="Email" - error={!values.email} - helperText={!values.email && 'Invalid email address'} + error={values.email} + helperText={values.email && 'Invalid email address'} onBlur={handleEmailChange} /> <TextField inputRef={passwordRef} label="Password" required - error={!values.password} - helperText={!values.password && 'Should be at least 6 characters'} + error={values.password} + helperText={values.password && 'Should be at least 6 characters'} type={showPassword ? 'text' : 'password'} onBlur={handlePasswordChange} InputProps={{ @@ -146,7 +150,7 @@ const Registration: React.FC = () => { ) }} /> - <Button variant="contained" type="submit" >submit</Button> + <Button variant="contained" type="submit">submit</Button> </form> <div className={classes.formTransfer}> <div>Already have an account?</div> -- cgit v1.2.3 From 421a1e8d73714dce004e7682dfa3c52ccdb0f71f Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 20:15:25 +0300 Subject: convert username input to lowercase --- src/containers/Registration/Registration.tsx | 55 +++++++++++++++------------- 1 file changed, 30 insertions(+), 25 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 955d900..ff9af60 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -44,13 +44,13 @@ const useStyles = makeStyles(theme => ({ })); interface ErrorStates { - username: boolean | undefined; - email: boolean | undefined; - password: boolean | undefined; + username: boolean; + email: boolean; + password: boolean; } const Registration: React.FC = () => { - const [values, setValues] = useState<ErrorStates>({ + const [errors, setErrors] = useState<ErrorStates>({ username: false, email: false, password: false @@ -65,10 +65,10 @@ const Registration: React.FC = () => { const history = useHistory(); const isValid = useMemo(() => { - return !values.username && !values.email && !values.password; - }, [values]); + return !errors.username && !errors.email && !errors.password; + }, [errors]); - const handleSubmit = (event:FormEvent<HTMLFormElement>) => { + const handleSubmit = (event: FormEvent<HTMLFormElement>) => { event.preventDefault(); const username = usernameRef.current?.value?.toLowerCase(); const password = passwordRef.current?.value; @@ -89,25 +89,29 @@ const Registration: React.FC = () => { const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => { event.preventDefault(); }; - const handleUsernameChange = (e: React.FocusEvent<HTMLInputElement>) => { - setValues({ - ...values, - username: e.currentTarget.value.length === 0 + const handleUsernameChange = (event: React.FocusEvent<HTMLInputElement>) => { + setErrors({ + ...errors, + username: event.currentTarget.value.length === 0 }); }; - const handleEmailChange = (e: React.FocusEvent<HTMLInputElement>) => { - setValues({ - ...values, - email: !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(e.currentTarget.value) + const handleEmailChange = (event: React.FocusEvent<HTMLInputElement>) => { + setErrors({ + ...errors, + email: !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(event.currentTarget.value) }); }; - const handlePasswordChange = (e: React.FocusEvent<HTMLInputElement>) => { - setValues({ - ...values, - password: e.currentTarget.value.length < 6 + const handlePasswordChange = (event: React.FocusEvent<HTMLInputElement>) => { + setErrors({ + ...errors, + password: event.currentTarget.value.length < 6 }); }; + const handleToLowerCase = (e: React.ChangeEvent<HTMLInputElement>) => { + e.target.value = ("" + e.target.value).toLowerCase(); + }; + return ( <> <div className={classes.formHeader}>Sign Up</div> @@ -115,24 +119,25 @@ const Registration: React.FC = () => { <TextField inputRef={usernameRef} label="Username" - error={values.username} - helperText={values.username && 'This field is required'} + error={errors.username} + helperText={errors.username && 'This field is required'} required onBlur={handleUsernameChange} + onInput={handleToLowerCase} /> <TextField inputRef={emailRef} label="Email" - error={values.email} - helperText={values.email && 'Invalid email address'} + error={errors.email} + helperText={errors.email && 'Invalid email address'} onBlur={handleEmailChange} /> <TextField inputRef={passwordRef} label="Password" required - error={values.password} - helperText={values.password && 'Should be at least 6 characters'} + error={errors.password} + helperText={errors.password && 'Should be at least 6 characters'} type={showPassword ? 'text' : 'password'} onBlur={handlePasswordChange} InputProps={{ -- cgit v1.2.3 From ceff44e622a07c5c17484e0222c78785fe12e6d2 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Fri, 28 Aug 2020 20:19:25 +0300 Subject: fix eslint errors --- src/containers/Registration/Registration.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index ff9af60..709d04e 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -109,7 +109,7 @@ const Registration: React.FC = () => { }; const handleToLowerCase = (e: React.ChangeEvent<HTMLInputElement>) => { - e.target.value = ("" + e.target.value).toLowerCase(); + e.target.value = e.target.value.toString().toLowerCase(); }; return ( -- cgit v1.2.3 From ae005b8444aacc4071512f38031043156b6854c4 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Sat, 29 Aug 2020 00:22:27 +0300 Subject: hide error state onFocue input --- src/containers/Registration/Registration.tsx | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 709d04e..e7c8874 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -44,16 +44,16 @@ const useStyles = makeStyles(theme => ({ })); interface ErrorStates { - username: boolean; - email: boolean; - password: boolean; + username: boolean | undefined; + email: boolean | undefined; + password: boolean | undefined; } const Registration: React.FC = () => { const [errors, setErrors] = useState<ErrorStates>({ - username: false, - email: false, - password: false + username: undefined, + email: undefined, + password: undefined }); const [showPassword, setShowPassword] = useState<boolean>(false); @@ -112,6 +112,13 @@ const Registration: React.FC = () => { e.target.value = e.target.value.toString().toLowerCase(); }; + const handleFocus = (value: string) => () => { + setErrors({ + ...errors, + [value]: undefined + }); + }; + return ( <> <div className={classes.formHeader}>Sign Up</div> @@ -124,6 +131,7 @@ const Registration: React.FC = () => { required onBlur={handleUsernameChange} onInput={handleToLowerCase} + onFocus={handleFocus('username')} /> <TextField inputRef={emailRef} @@ -131,6 +139,7 @@ const Registration: React.FC = () => { error={errors.email} helperText={errors.email && 'Invalid email address'} onBlur={handleEmailChange} + onFocus={handleFocus('email')} /> <TextField inputRef={passwordRef} @@ -140,6 +149,7 @@ const Registration: React.FC = () => { helperText={errors.password && 'Should be at least 6 characters'} type={showPassword ? 'text' : 'password'} onBlur={handlePasswordChange} + onFocus={handleFocus('password')} InputProps={{ endAdornment: ( <InputAdornment position="end"> -- cgit v1.2.3 From e3d230ffcc344b9027b19a136525f105149f0982 Mon Sep 17 00:00:00 2001 From: ilyayudovin <ilyayudovin123@gmail.com> Date: Sat, 29 Aug 2020 02:20:22 +0300 Subject: add fixed height for inputs to avoid jumping errorText --- src/containers/Registration/Registration.tsx | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index e7c8874..422cf92 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -40,6 +40,9 @@ const useStyles = makeStyles(theme => ({ marginLeft: 10, color: 'green', cursor: 'pointer' + }, + textField: { + height: 70 } })); @@ -132,6 +135,7 @@ const Registration: React.FC = () => { onBlur={handleUsernameChange} onInput={handleToLowerCase} onFocus={handleFocus('username')} + className={classes.textField} /> <TextField inputRef={emailRef} @@ -140,6 +144,7 @@ const Registration: React.FC = () => { helperText={errors.email && 'Invalid email address'} onBlur={handleEmailChange} onFocus={handleFocus('email')} + className={classes.textField} /> <TextField inputRef={passwordRef} @@ -164,6 +169,7 @@ const Registration: React.FC = () => { </InputAdornment> ) }} + className={classes.textField} /> <Button variant="contained" type="submit">submit</Button> </form> -- cgit v1.2.3 From 868e380262dcef15d9645ceb912d18701ecb61fb Mon Sep 17 00:00:00 2001 From: eug-vs <eug-vs@keemail.me> Date: Sat, 29 Aug 2020 14:29:28 +0300 Subject: feat: use yup and formik --- src/containers/Registration/Registration.tsx | 203 +++++++++++---------------- 1 file changed, 84 insertions(+), 119 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index 422cf92..b5c56e6 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -1,10 +1,7 @@ -import React, { - useState, - useRef, - FormEvent, - useMemo -} from 'react'; +import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; +import { Formik, Form, Field } from 'formik'; +import * as Yup from 'yup'; import { makeStyles } from '@material-ui/core/styles'; import { TextField, @@ -16,6 +13,22 @@ import { Visibility, VisibilityOff } from '@material-ui/icons'; import { post } from '../../requests'; import { useAuth } from '../../hooks/useAuth'; +interface Fields { + username: string; + email: string; + password: string; +} + +const validationSchema = Yup.object({ + username: Yup.string() + .required('This field is required'), + email: Yup.string() + .email('Invalid email address') + .required('This field is required'), + password: Yup.string() + .min(6, 'Should be at least 6 characters') + .required('This field is required'), +}); const useStyles = makeStyles(theme => ({ root: { @@ -42,137 +55,89 @@ const useStyles = makeStyles(theme => ({ cursor: 'pointer' }, textField: { - height: 70 + height: theme.spacing(8) } })); -interface ErrorStates { - username: boolean | undefined; - email: boolean | undefined; - password: boolean | undefined; -} - const Registration: React.FC = () => { - const [errors, setErrors] = useState<ErrorStates>({ - username: undefined, - email: undefined, - password: undefined - }); - const [showPassword, setShowPassword] = useState<boolean>(false); - const classes = useStyles(); - const usernameRef = useRef<HTMLInputElement>(); - const emailRef = useRef<HTMLInputElement>(); - const passwordRef = useRef<HTMLInputElement>(); const { login } = useAuth(); const history = useHistory(); - - const isValid = useMemo(() => { - return !errors.username && !errors.email && !errors.password; - }, [errors]); - - const handleSubmit = (event: FormEvent<HTMLFormElement>) => { - event.preventDefault(); - const username = usernameRef.current?.value?.toLowerCase(); - const password = passwordRef.current?.value; - const email = emailRef.current?.value; - if (username && password && isValid) { - post('/users', { username, password, email }) - .then(() => login(username, password)) - .then(() => history.push(`/profile/${username}`)); - } - }; + const [showPassword, setShowPassword] = useState<boolean>(false); const handleLogin = () => { history.push('/login'); }; - const handleClickShowPassword = () => { - setShowPassword(prevState => !prevState); - }; - const handleMouseDownPassword = (event: React.MouseEvent<HTMLButtonElement>) => { - event.preventDefault(); - }; - const handleUsernameChange = (event: React.FocusEvent<HTMLInputElement>) => { - setErrors({ - ...errors, - username: event.currentTarget.value.length === 0 - }); - }; - const handleEmailChange = (event: React.FocusEvent<HTMLInputElement>) => { - setErrors({ - ...errors, - email: !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(event.currentTarget.value) - }); - }; - const handlePasswordChange = (event: React.FocusEvent<HTMLInputElement>) => { - setErrors({ - ...errors, - password: event.currentTarget.value.length < 6 - }); - }; - const handleToLowerCase = (e: React.ChangeEvent<HTMLInputElement>) => { - e.target.value = e.target.value.toString().toLowerCase(); - }; + const handleSubmit = ({ username, email, password }: Fields) => { + post('/users', { username, email, password }) + .then(() => login(username, password)) + .then(() => history.push(`/profile/${username}`)); + } - const handleFocus = (value: string) => () => { - setErrors({ - ...errors, - [value]: undefined - }); + const handleClickShowPassword = () => { + setShowPassword(prevState => !prevState); }; return ( <> <div className={classes.formHeader}>Sign Up</div> - <form className={classes.root} noValidate autoComplete="off" onSubmit={handleSubmit}> - <TextField - inputRef={usernameRef} - label="Username" - error={errors.username} - helperText={errors.username && 'This field is required'} - required - onBlur={handleUsernameChange} - onInput={handleToLowerCase} - onFocus={handleFocus('username')} - className={classes.textField} - /> - <TextField - inputRef={emailRef} - label="Email" - error={errors.email} - helperText={errors.email && 'Invalid email address'} - onBlur={handleEmailChange} - onFocus={handleFocus('email')} - className={classes.textField} - /> - <TextField - inputRef={passwordRef} - label="Password" - required - error={errors.password} - helperText={errors.password && 'Should be at least 6 characters'} - type={showPassword ? 'text' : 'password'} - onBlur={handlePasswordChange} - onFocus={handleFocus('password')} - InputProps={{ - endAdornment: ( - <InputAdornment position="end"> - <IconButton - size="small" - aria-label="toggle password visibility" - onClick={handleClickShowPassword} - onMouseDown={handleMouseDownPassword} - > - {showPassword ? <Visibility /> : <VisibilityOff />} - </IconButton> - </InputAdornment> - ) - }} - className={classes.textField} - /> - <Button variant="contained" type="submit">submit</Button> - </form> + <Formik + initialValues={{ username: '', email: '', password: '' }} + validationSchema={validationSchema} + onSubmit={handleSubmit} + > + {({ values, errors, touched, isSubmitting }) => ( + <Form className={classes.root}> + <Field + id="username" + name="username" + label="Username" + value={values.username.toLowerCase()} + error={touched.username && !!errors.username} + helperText={touched.username && errors.username} + required + className={classes.textField} + as={TextField} + /> + <Field + name="email" + label="Email" + value={values.email} + error={touched.email && !!errors.email} + helperText={touched.email && errors.email} + required + className={classes.textField} + as={TextField} + /> + <Field + name="password" + label="Password" + value={values.password} + error={touched.password && !!errors.password} + helperText={touched.password && errors.password} + required + type={showPassword ? 'text' : 'password'} + as={TextField} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + <IconButton + size="small" + aria-label="toggle password visibility" + onClick={handleClickShowPassword} + > + {showPassword ? <Visibility /> : <VisibilityOff />} + </IconButton> + </InputAdornment> + ) + }} + className={classes.textField} + /> + <Button variant="contained" type="submit" disabled={isSubmitting}>submit</Button> + </Form> + )} + </Formik> <div className={classes.formTransfer}> <div>Already have an account?</div> <span -- cgit v1.2.3 From bf7c63cd53dde93bffe24eb1426b8bfc2037646b Mon Sep 17 00:00:00 2001 From: eug-vs <eug-vs@keemail.me> Date: Sat, 29 Aug 2020 15:30:48 +0300 Subject: feat: use Formik in Login form --- src/containers/Registration/Registration.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index b5c56e6..fb2dc8c 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -21,6 +21,7 @@ interface Fields { const validationSchema = Yup.object({ username: Yup.string() + .lowercase('Must be lowercase') .required('This field is required'), email: Yup.string() .email('Invalid email address') @@ -54,9 +55,6 @@ const useStyles = makeStyles(theme => ({ color: 'green', cursor: 'pointer' }, - textField: { - height: theme.spacing(8) - } })); const Registration: React.FC = () => { @@ -75,7 +73,7 @@ const Registration: React.FC = () => { .then(() => history.push(`/profile/${username}`)); } - const handleClickShowPassword = () => { + const toggleShowPassword = () => { setShowPassword(prevState => !prevState); }; @@ -88,7 +86,7 @@ const Registration: React.FC = () => { onSubmit={handleSubmit} > {({ values, errors, touched, isSubmitting }) => ( - <Form className={classes.root}> + <Form className={classes.root} autoComplete="off"> <Field id="username" name="username" @@ -97,7 +95,6 @@ const Registration: React.FC = () => { error={touched.username && !!errors.username} helperText={touched.username && errors.username} required - className={classes.textField} as={TextField} /> <Field @@ -107,7 +104,6 @@ const Registration: React.FC = () => { error={touched.email && !!errors.email} helperText={touched.email && errors.email} required - className={classes.textField} as={TextField} /> <Field @@ -122,17 +118,12 @@ const Registration: React.FC = () => { InputProps={{ endAdornment: ( <InputAdornment position="end"> - <IconButton - size="small" - aria-label="toggle password visibility" - onClick={handleClickShowPassword} - > + <IconButton size="small" onClick={toggleShowPassword} > {showPassword ? <Visibility /> : <VisibilityOff />} </IconButton> </InputAdornment> ) }} - className={classes.textField} /> <Button variant="contained" type="submit" disabled={isSubmitting}>submit</Button> </Form> -- cgit v1.2.3 From b7aed6ed7df2cea67924d34154671afb75edb7c2 Mon Sep 17 00:00:00 2001 From: eug-vs <eug-vs@keemail.me> Date: Sat, 29 Aug 2020 15:34:17 +0300 Subject: fix: resovle eslint errors --- src/containers/Registration/Registration.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/containers/Registration') diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx index fb2dc8c..a3aedb3 100644 --- a/src/containers/Registration/Registration.tsx +++ b/src/containers/Registration/Registration.tsx @@ -28,7 +28,7 @@ const validationSchema = Yup.object({ .required('This field is required'), password: Yup.string() .min(6, 'Should be at least 6 characters') - .required('This field is required'), + .required('This field is required') }); const useStyles = makeStyles(theme => ({ @@ -54,7 +54,7 @@ const useStyles = makeStyles(theme => ({ marginLeft: 10, color: 'green', cursor: 'pointer' - }, + } })); const Registration: React.FC = () => { @@ -71,7 +71,7 @@ const Registration: React.FC = () => { post('/users', { username, email, password }) .then(() => login(username, password)) .then(() => history.push(`/profile/${username}`)); - } + }; const toggleShowPassword = () => { setShowPassword(prevState => !prevState); @@ -118,7 +118,7 @@ const Registration: React.FC = () => { InputProps={{ endAdornment: ( <InputAdornment position="end"> - <IconButton size="small" onClick={toggleShowPassword} > + <IconButton size="small" onClick={toggleShowPassword}> {showPassword ? <Visibility /> : <VisibilityOff />} </IconButton> </InputAdornment> -- cgit v1.2.3