From 12c41251c17c86d3d40bba83a7e7b174870bc3f1 Mon Sep 17 00:00:00 2001
From: Ben Wheeler <wheeler.benjamin@gmail.com>
Date: Sat, 10 Aug 2019 09:55:59 -0400
Subject: [PATCH] Add formik checkbox component, Show password checkbox

---
 .../formik-forms/formik-checkbox.jsx          | 84 +++++++++++++++++++
 .../formik-forms/formik-checkbox.scss         | 39 +++++++++
 src/components/formik-forms/formik-forms.scss |  3 +
 .../formik-forms/formik-radio-button.jsx      |  2 +
 .../formik-forms/formik-radio-button.scss     |  1 -
 src/components/join-flow/join-flow-steps.scss |  4 +
 src/components/join-flow/username-step.jsx    | 29 ++++---
 7 files changed, 146 insertions(+), 16 deletions(-)
 create mode 100644 src/components/formik-forms/formik-checkbox.jsx
 create mode 100644 src/components/formik-forms/formik-checkbox.scss
 create mode 100644 src/components/formik-forms/formik-forms.scss

diff --git a/src/components/formik-forms/formik-checkbox.jsx b/src/components/formik-forms/formik-checkbox.jsx
new file mode 100644
index 000000000..6c9d3bd3c
--- /dev/null
+++ b/src/components/formik-forms/formik-checkbox.jsx
@@ -0,0 +1,84 @@
+const classNames = require('classnames');
+const PropTypes = require('prop-types');
+const React = require('react');
+import {Field} from 'formik';
+
+require('./formik-checkbox.scss');
+require('./formik-forms.scss');
+require('../forms/row.scss');
+
+const FormikCheckboxSubComponent = ({
+    className,
+    field,
+    id,
+    label,
+    ...props
+}) => (
+    <div className="checkbox">
+        <input
+            checked={field.value}
+            className={classNames(
+                'formik-checkbox',
+                className
+            )}
+            id={id}
+            name={field.name}
+            type="checkbox"
+            value={field.value}
+            onBlur={field.onBlur} /* eslint-disable-line react/jsx-handler-names */
+            onChange={field.onChange} /* eslint-disable-line react/jsx-handler-names */
+            {...props}
+        />
+        {label && (
+            <label
+                className={classNames(
+                    'formik-label',
+                    'formik-checkbox-label'
+                )}
+                htmlFor={id}
+            >
+                {label}
+            </label>
+        )}
+    </div>
+);
+
+FormikCheckboxSubComponent.propTypes = {
+    className: PropTypes.string,
+    field: PropTypes.shape({
+        name: PropTypes.string,
+        onBlur: PropTypes.function,
+        onChange: PropTypes.function,
+        value: PropTypes.bool
+    }),
+    id: PropTypes.string,
+    label: PropTypes.string,
+    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
+};
+
+
+const FormikCheckbox = ({
+    className,
+    id,
+    label,
+    name,
+    ...props
+}) => (
+    <Field
+        className={className}
+        component={FormikCheckboxSubComponent}
+        id={id}
+        label={label}
+        name={name}
+        {...props}
+    />
+);
+
+FormikCheckbox.propTypes = {
+    className: PropTypes.string,
+    id: PropTypes.string,
+    label: PropTypes.string,
+    name: PropTypes.string
+};
+
+module.exports = FormikCheckbox;
diff --git a/src/components/formik-forms/formik-checkbox.scss b/src/components/formik-forms/formik-checkbox.scss
new file mode 100644
index 000000000..66c302587
--- /dev/null
+++ b/src/components/formik-forms/formik-checkbox.scss
@@ -0,0 +1,39 @@
+@import "../../colors";
+
+.formik-checkbox-label {
+    font-weight: 300;
+}
+
+input[type="checkbox"].formik-checkbox {
+    display: block;
+    float: left;
+    margin-right: .625rem;
+    border: 1px solid $active-dark-gray;
+    border-radius: 3px;
+    width: 1.25rem;
+    height: 1.25rem;
+    appearance: none;
+
+    &:focus:checked {
+        transition: all .5s ease;
+        outline: none;
+        box-shadow: 0 0 0 .25rem $ui-blue-25percent;
+    }
+
+    &:focus:not(:checked) {
+        outline: none;
+    }
+
+    &:checked {
+        background-color: $ui-blue;
+        text-align: center;
+        text-indent: .125rem;
+        line-height: 1.25rem;
+        font-size: .75rem;
+
+        &:after {
+            color: $type-white;
+            content: "\2714";
+        }
+    }
+}
diff --git a/src/components/formik-forms/formik-forms.scss b/src/components/formik-forms/formik-forms.scss
new file mode 100644
index 000000000..a084812cc
--- /dev/null
+++ b/src/components/formik-forms/formik-forms.scss
@@ -0,0 +1,3 @@
+.formik-label {
+    font-weight: 500;
+}
diff --git a/src/components/formik-forms/formik-radio-button.jsx b/src/components/formik-forms/formik-radio-button.jsx
index d714ed31a..70b27fb9b 100644
--- a/src/components/formik-forms/formik-radio-button.jsx
+++ b/src/components/formik-forms/formik-radio-button.jsx
@@ -5,6 +5,7 @@ import {Field} from 'formik';
 
 const FormikInput = require('./formik-input.jsx');
 
+require('./formik-forms.scss');
 require('./formik-radio-button.scss');
 require('../forms/row.scss');
 
@@ -34,6 +35,7 @@ const FormikRadioButtonSubComponent = ({
         {label && (
             <label
                 className={classNames(
+                    'formik-label',
                     'formik-radio-label',
                     labelClassName
                 )}
diff --git a/src/components/formik-forms/formik-radio-button.scss b/src/components/formik-forms/formik-radio-button.scss
index 9fb103a74..c178cf630 100644
--- a/src/components/formik-forms/formik-radio-button.scss
+++ b/src/components/formik-forms/formik-radio-button.scss
@@ -1,7 +1,6 @@
 @import "../../colors";
 
 .formik-radio-label {
-    font-weight: 300;
     margin-left: 1rem;
 }
 
diff --git a/src/components/join-flow/join-flow-steps.scss b/src/components/join-flow/join-flow-steps.scss
index 06ff7b4e3..ce124b89e 100644
--- a/src/components/join-flow/join-flow-steps.scss
+++ b/src/components/join-flow/join-flow-steps.scss
@@ -13,6 +13,10 @@
     }
 }
 
+.join-flow-password-confirm {
+    margin-bottom: .6875rem;
+}
+
 .join-flow-input-tall {
     height: 3rem;
 }
diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx
index 9d5199787..33092f57c 100644
--- a/src/components/join-flow/username-step.jsx
+++ b/src/components/join-flow/username-step.jsx
@@ -7,6 +7,7 @@ const {injectIntl, intlShape} = require('react-intl');
 
 const validate = require('../../lib/validate');
 const FormikInput = require('../../components/formik-forms/formik-input.jsx');
+const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx');
 const JoinFlowStep = require('./join-flow-step.jsx');
 
 require('./join-flow-steps.scss');
@@ -26,9 +27,6 @@ class UsernameStep extends React.Component {
             'validateUsernameIfPresent',
             'validateForm'
         ]);
-        this.state = {
-            showPassword: false
-        };
     }
     handleChangeShowPassword () {
         this.setState({showPassword: !this.state.showPassword});
@@ -88,6 +86,7 @@ class UsernameStep extends React.Component {
     // called after all validations pass with no errors
     handleValidSubmit (formData, formikBag) {
         formikBag.setSubmitting(false); // formik makes us do this ourselves
+        delete formData.showPassword;
         this.props.onNextStep(formData);
     }
     render () {
@@ -96,7 +95,8 @@ class UsernameStep extends React.Component {
                 initialValues={{
                     username: '',
                     password: '',
-                    passwordConfirm: ''
+                    passwordConfirm: '',
+                    showPassword: false
                 }}
                 validate={this.validateForm}
                 validateOnBlur={false}
@@ -154,7 +154,7 @@ class UsernameStep extends React.Component {
                                         error={errors.password}
                                         id="password"
                                         name="password"
-                                        type={this.state.showPassword ? 'text' : 'password'}
+                                        type={values.showPassword ? 'text' : 'password'}
                                         /* eslint-disable react/jsx-no-bind */
                                         validate={password => this.validatePasswordIfPresent(password, values.username)}
                                         validationClassName="validation-full-width-input"
@@ -167,12 +167,14 @@ class UsernameStep extends React.Component {
                                     />
                                     <FormikInput
                                         className={classNames(
-                                            'join-flow-input'
+                                            'join-flow-input',
+                                            'join-flow-password-confirm',
+                                            {fail: errors.passwordConfirm}
                                         )}
                                         error={errors.passwordConfirm}
                                         id="passwordConfirm"
                                         name="passwordConfirm"
-                                        type={this.state.showPassword ? 'text' : 'password'}
+                                        type={values.showPassword ? 'text' : 'password'}
                                         /* eslint-disable react/jsx-no-bind */
                                         validate={() =>
                                             this.validatePasswordConfirmIfPresent(values.password,
@@ -189,14 +191,11 @@ class UsernameStep extends React.Component {
                                         /* eslint-enable react/jsx-no-bind */
                                     />
                                     <div className="join-flow-input-title">
-                                        <div
-                                            onClick={this.handleChangeShowPassword}
-                                        >
-                                            {/* TODO: should localize 'Hide password' if we use that */}
-                                            {this.state.showPassword ? 'Hide password' : (
-                                                this.props.intl.formatMessage({id: 'registration.showPassword'})
-                                            )}
-                                        </div>
+                                        <FormikCheckbox
+                                            id="showPassword"
+                                            label={this.props.intl.formatMessage({id: 'registration.showPassword'})}
+                                            name="showPassword"
+                                        />
                                     </div>
                                 </div>
                             </div>