Skip to content

Commit

Permalink
signup password indicator and strength bar
Browse files Browse the repository at this point in the history
  • Loading branch information
snyaggarwal committed Feb 15, 2021
1 parent 65f4d2d commit c40ef4d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 1 deletion.
23 changes: 23 additions & 0 deletions src/components/common/HTMLTooltip.jsx
@@ -0,0 +1,23 @@
import { Tooltip } from '@material-ui/core';
import { withStyles } from '@material-ui/core/styles';

const HtmlTooltip = withStyles(theme => ({
tooltip: {
backgroundColor: '#f5f5f9',
color: 'rgba(0, 0, 0, 0.87)',
maxWidth: 220,
fontSize: theme.typography.pxToRem(12),
border: '1px solid rgb(51, 115, 170)',
left: '-5px',
display: 'flex',
alignItems: 'center',
},
arrow: {
color: '#f5f5f9',
"&::before": {
border: '1px solid rgb(51, 115, 170)',
}
}
}))(Tooltip);

export default HtmlTooltip;
54 changes: 54 additions & 0 deletions src/components/common/PasswordFields.jsx
@@ -0,0 +1,54 @@
import React from 'react';
import PasswordStrengthBar from 'react-password-strength-bar';
import { TextField } from '@material-ui/core';
import { get } from 'lodash';
import HtmlTooltip from './HtmlTooltip';
import PasswordValidatorIndicator from './PasswordValidatorIndicator';

const PasswordFields = ({
onChange, password, passwordErrors, passwordFieldId, confirmPasswordFieldId
}) => {
const [tooltip, setTooltip] = React.useState(false);
const onBlur = () => setTooltip(true);
const onFocus = () => setTooltip(true)
return (
<React.Fragment>
<div style={{marginTop: '10px'}}>
<HtmlTooltip
arrow
placement='right'
open={tooltip}
title={<PasswordValidatorIndicator password={password} />}
>
<TextField
required
error={Boolean(passwordErrors)}
helperText={get(passwordErrors, '0')}
label="Password"
variant="outlined"
id={passwordFieldId}
onChange={onChange}
type="password"
fullWidth
onFocus={onFocus}
onBlur={onBlur}
/>
</HtmlTooltip>
</div>
<div style={{marginTop: '10px'}}>
<TextField
required
label="Confirm Password"
variant="outlined"
id={confirmPasswordFieldId}
onChange={onChange}
type="password"
fullWidth
/>
</div>
<PasswordStrengthBar password={password} minLength={8} className='password-strength' />
</React.Fragment>
)
}

export default PasswordFields;
44 changes: 44 additions & 0 deletions src/components/common/PasswordValidatorIndicator.jsx
@@ -0,0 +1,44 @@
import React from 'react';
import {
CheckCircle as CorrectIcon, Cancel as WrongIcon
} from '@material-ui/icons';
import { ERROR_RED, BLUE } from '../../common/constants';
import { merge } from 'lodash';

const correctIcon = <CorrectIcon color='primary' fontSize='inherit'/>;
const incorrectIcon = <WrongIcon fontSize='inherit' style={{color: ERROR_RED}}/>;

const Indicator = (predicate, label) => {
const commonSpanStyles = {marginLeft: '5px'}
const getStyles = predicate => predicate ? {color: BLUE} : {color: ERROR_RED};

return (
<div className='flex-vertical-center'>
{
predicate ? correctIcon : incorrectIcon
}
<span style={merge(commonSpanStyles, getStyles(predicate))}>
{label}
</span>
</div>
)}

const PasswordValidatorIndicator = ({password}) => {
const hasMinLength = Boolean(password && password.length >= 8)
const hasAlphabets = Boolean(password && password.match(new RegExp(/[a-zA-Z]/g)))

return (
<React.Fragment>
<div className='col-md-12 no-side-padding' style={{fontSize: '12px'}}>
{
Indicator(hasMinLength, 'Must be min 8 Characters.')
}
{
Indicator(hasAlphabets, 'Must have alphabets (a-zA-Z).')
}
</div>
</React.Fragment>
)
}

export default PasswordValidatorIndicator;
20 changes: 19 additions & 1 deletion src/components/users/Signup.jsx
Expand Up @@ -5,6 +5,7 @@ import { TextField, Button, Paper } from '@material-ui/core';
import {set, get, isEmpty, cloneDeep, startCase, map, includes} from 'lodash';
import APIService from '../../services/APIService';
import VerifyEmailMessage from './VerifyEmailMessage';
import PasswordFields from '../common/PasswordFields';

class Signup extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -34,6 +35,11 @@ class Signup extends React.Component {
this.setState(newState)
}

isPasswordValid() {
const { password } = this.state.fields;
return Boolean(password && password.length >=8 && password.match(new RegExp(/[a-zA-Z]/g)))
}

onSubmit = event => {
event.preventDefault();
event.stopPropagation();
Expand All @@ -47,6 +53,11 @@ class Signup extends React.Component {
this.setState({serverError: ['Password and Confirm Password must match.']})
return
}
if(!this.isPasswordValid()) {
this.setState({serverError: ['Invalid Password']})
return
}


if(isFormValid && isConfirmPasswordSameAsPassword) {
this.setState({serverError: null}, () => {
Expand Down Expand Up @@ -84,7 +95,7 @@ class Signup extends React.Component {
<div className='col-md-12 no-side-padding'>
<form>
{
map(['first_name', 'last_name', 'username', 'email', 'password', 'confirm_password'], (attr, index) => (
map(['first_name', 'last_name', 'username', 'email'], (attr, index) => (
<div style={index !== 0 ? {marginTop: '10px'} : {}} key={attr}>
<TextField
required
Expand All @@ -100,6 +111,13 @@ class Signup extends React.Component {
</div>
))
}
<PasswordFields
onChange={event => this.setFieldValue(event.target.id, event.target.value)}
passwordErrors={get(fieldErrors, 'password')}
passwordFieldId="fields.password"
confirmPasswordFieldId="fields.confirm_password"
password={fields.password}
/>
<div style={{marginTop: '20px', textAlign: 'center', marginBottom: '20px'}}>
<Button onClick={this.onSubmit} type='submit' color='primary' variant='contained'>Sign Up</Button>
<div style={{marginTop: '15px'}}>
Expand Down

0 comments on commit c40ef4d

Please sign in to comment.