Skip to content

Commit

Permalink
Merge pull request kobotoolbox#2043 from kobotoolbox/password-strength
Browse files Browse the repository at this point in the history
Password strength in registration form
  • Loading branch information
jnm authored Dec 10, 2018
2 parents a2b3b56 + 94540d5 commit ab6e8c4
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 1 deletion.
7 changes: 7 additions & 0 deletions jsapp/js/bem.es6
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@ bem.Radio__row = bem.Radio.__('row', '<label>');
bem.Radio__input = bem.Radio.__('input', '<input>');
bem.Radio__label = bem.Radio.__('label', '<span>');

bem.PasswordStrength = bem('password-strength');
bem.PasswordStrength__title = bem.PasswordStrength.__('title');
bem.PasswordStrength__bar = bem.PasswordStrength.__('bar');
bem.PasswordStrength__indicator = bem.PasswordStrength.__('indicator');
bem.PasswordStrength__messages = bem.PasswordStrength.__('messages', '<ul>');
bem.PasswordStrength__message = bem.PasswordStrength.__('message', '<li>');

bem.PrintOnly = BEM('print-only');

bem.GitRev = BEM('git-rev');
Expand Down
54 changes: 54 additions & 0 deletions jsapp/js/components/passwordStrength.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import autoBind from 'react-autobind';
import zxcvbn from 'zxcvbn';
import bem from '../bem';
import {t} from '../utils';

/*
Properties:
- password <string>: required
*/
class PasswordStrength extends React.Component {
constructor(props){
super(props);
autoBind(this);
}

render() {
const report = zxcvbn(this.props.password);
const barModifier = `score-${report.score}`;
return (
<bem.PasswordStrength>
<bem.PasswordStrength__title>
{t('Password strength')}
</bem.PasswordStrength__title>

<bem.PasswordStrength__bar m={barModifier}>
<bem.PasswordStrength__indicator/>
</bem.PasswordStrength__bar>

{(report.feedback.warning || report.feedback.suggestions.length > 0) &&
<bem.PasswordStrength__messages>
{report.feedback.warning &&
<bem.PasswordStrength__message m='warning'>
{report.feedback.warning}
</bem.PasswordStrength__message>
}

{report.feedback.suggestions.length > 0 &&
report.feedback.suggestions.map((suggestion, index) => {
return (
<bem.PasswordStrength__message key={index}>
{suggestion}
</bem.PasswordStrength__message>
)
})
}
</bem.PasswordStrength__messages>
}
</bem.PasswordStrength>
)
}
}

export default PasswordStrength;
8 changes: 8 additions & 0 deletions jsapp/js/main.es6
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import RunRoutes, {routes} from './app';
import RegistrationPasswordApp from './registrationPasswordApp';
import {AppContainer} from 'react-hot-loader'
import $ from 'jquery';
import 'babel-polyfill'; // required to support Array.prototypes.includes in IE11
Expand Down Expand Up @@ -43,3 +44,10 @@ if (document.head.querySelector('meta[name=kpi-root-url]')) {
} else {
console.error('no kpi-root-url meta tag set. skipping react-router init');
}

document.addEventListener('DOMContentLoaded', (evt) => {
const registrationPasswordAppEl = document.getElementById('registration-password-app');
if (registrationPasswordAppEl) {
render(<AppContainer><RegistrationPasswordApp /></AppContainer>, registrationPasswordAppEl);
}
});
34 changes: 34 additions & 0 deletions jsapp/js/registrationPasswordApp.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import PasswordStrength from './components/passwordStrength';

const PASS_INPUT_ID = 'id_password1';

class RegistrationPasswordApp extends React.Component {
constructor(props) {
super(props);
this.state = {currentPass: ''}
}

componentDidMount() {
this.watchInput(PASS_INPUT_ID);
}

watchInput(inputId) {
const inputEl = document.getElementById(inputId);
if (inputEl) {
inputEl.addEventListener('input', (evt) => {
this.setState({currentPass: evt.currentTarget.value})
});
} else {
throw new Error(`Input "${inputId}" not found!`);
}
}

render() {
return (
<PasswordStrength password={this.state.currentPass} />
);
}
};

export default RegistrationPasswordApp;
63 changes: 63 additions & 0 deletions jsapp/scss/components/_kobo.password-strength.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.password-strength {
width: 100%;
font-size: 0.9em;
}

.password-strength__title,
.password-strength__bar {
margin-bottom: 10px;
}

.password-strength__bar {
width: 100%;
height: 5px;
position: relative;
box-shadow: inset 0 0 0 1px $cool-silver;

.password-strength__indicator {
background: blue;
position: absolute;
top: 0;
left: 0;
height: 100%;
transition: 0.25s;
}

&.password-strength__bar--score-0 {
.password-strength__indicator {
width: 1%;
background-color: $cool-red;
}
}

&.password-strength__bar--score-1 {
.password-strength__indicator {
width: 25%;
background-color: $cool-red;
}
}
&.password-strength__bar--score-2 {
.password-strength__indicator {
width: 50%;
background-color: $cool-orange;
}
}
&.password-strength__bar--score-3 {
.password-strength__indicator {
width: 75%;
background-color: $warm-green;
}
}
&.password-strength__bar--score-4 {
.password-strength__indicator {
width: 100%;
background-color: $cool-blue;
}
}
}

.password-strength__message {
&.password-strength__message--warning {
color: $cool-red;
}
}
1 change: 1 addition & 0 deletions jsapp/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
@import "./components/kobo.lib-list";
@import "./components/kobo.form-view";
@import "./components/kobo.form-summary";
@import "./components/kobo.password-strength";
@import "./components/kobo.report-view";
@import "./components/kobo.table";
@import "./components/kobo.account-settings";
Expand Down
4 changes: 4 additions & 0 deletions kpi/templates/registration/registration_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ <h1>{% trans "Create an account" %}</h1>
{% endif %}
{{ field.errors }}
</div>

{% if field.name == 'password1' %}
<div class="field" id="registration-password-app"></div>
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'Create Account' %}" class="registration__action" />
<div class="registration__orlogin">
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"webpack": "^4.20.2",
"webpack-bundle-tracker": "0.4.0-beta",
"webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.8"
"webpack-dev-server": "^3.1.8",
"zxcvbn": "^4.4.2"
},
"scripts": {
"build": "webpack --config webpack/prod.config.js --progress --colors",
Expand Down

0 comments on commit ab6e8c4

Please sign in to comment.