Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-2206: Onboarding - Step 1 - Create Account #639

Merged
merged 36 commits into from Dec 7, 2020
Merged
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
834bb52
Make form
benstrumeyer Dec 1, 2020
3300d83
Add skip and learn more links
benstrumeyer Dec 1, 2020
5911f47
Set arrow up/down depending on expanded state
benstrumeyer Dec 1, 2020
a56094f
Make FAQ
benstrumeyer Dec 1, 2020
f623f22
Make list responsive
benstrumeyer Dec 1, 2020
9d641f9
Adjust padding of FAQContainer to scroll onto nice spot
benstrumeyer Dec 1, 2020
f9ad55e
Make sure promoString checkbox text does not wrap
benstrumeyer Dec 1, 2020
13d10ee
Add privacy policy link
benstrumeyer Dec 1, 2020
11f908c
Make privacy policy link more responsive
benstrumeyer Dec 1, 2020
6cfcdce
Make BrowserLogInForm and style responsively
benstrumeyer Dec 2, 2020
9f024a5
Refactor Create Account form into a component
benstrumeyer Dec 2, 2020
0371513
Create BrowserCreateAccountFormContainer and add handlers
benstrumeyer Dec 2, 2020
7a04bd3
Add error handling to legal consent label
benstrumeyer Dec 2, 2020
31b8e8e
Remove privacy policy link if learn more is not expanded
benstrumeyer Dec 2, 2020
2071175
Add handlers for log in view and replace strings
benstrumeyer Dec 2, 2020
74b0a41
Add handlers for forgot password and throttle it
benstrumeyer Dec 2, 2020
7d9bd47
Lower throttle time in case user enters a wrong email
benstrumeyer Dec 2, 2020
ba6f3c9
Make updates checkbox work
benstrumeyer Dec 2, 2020
c098331
Add signed in view
benstrumeyer Dec 2, 2020
1087c46
Change spacing of title and subtitle for small screens
benstrumeyer Dec 2, 2020
b8c2629
Update create account design for small screens
benstrumeyer Dec 2, 2020
e187609
Center skip link on small screens
benstrumeyer Dec 2, 2020
a6e9257
Decrease spacing for small sreens on already have account/create acco…
benstrumeyer Dec 2, 2020
f715cf7
Decrease width of faqDescription items and privacy policy link
benstrumeyer Dec 2, 2020
8b6838d
Open privacy policy link in new tab
benstrumeyer Dec 2, 2020
817ce0b
Add confirm password functionality
benstrumeyer Dec 4, 2020
dfc6ec9
Update passwords not match string
benstrumeyer Dec 4, 2020
7ff259f
Add global email preferences to user object on account creation success
benstrumeyer Dec 4, 2020
143cce8
Hold input fields in place when error messages appear
benstrumeyer Dec 4, 2020
136ef0d
Merge branch 'ghostery-browser-intro-hub' into GH-2206
benstrumeyer Dec 4, 2020
eda38ca
Rename components
benstrumeyer Dec 4, 2020
91badf5
Add propTypes
benstrumeyer Dec 5, 2020
1b24823
Update comments
benstrumeyer Dec 5, 2020
6ce6eca
Remove newline
benstrumeyer Dec 5, 2020
44518ad
Update confirm password string
benstrumeyer Dec 5, 2020
6ff20ca
Add comments and minor refactors
benstrumeyer Dec 5, 2020
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

@@ -1722,6 +1722,9 @@
"hub_create_account_label_password_invalid_length": {
"message": "Use between 8 and 50 characters."
},
"hub_create_account_confirm_password_do_not_match": {
"message": "Your passwords do not match."
},
"hub_create_account_already_have_account": {
"message": "Already have a Ghostery Account?"
},
@@ -1758,6 +1761,66 @@
"ghostery_browser_hub_onboarding_lets_do_this": {
"message": "Let's do this"
},
"ghostery_browser_hub_onboarding_create_a_ghostery_account": {
"message": "Create a Ghostery Account"
},
"ghostery_browser_hub_onboarding_sync_settings": {
"message": "Allows you to sync settings across browsers and devices"
},
"ghostery_browser_hub_onboarding_already_have_account": {
"message": "I already have an account."
},
"ghostery_browser_hub_onboarding_send_me": {
"message": "Send me"
},
"ghostery_browser_hub_onboarding_updates_and_promotions": {
"message": "updates & promotions."
},
"ghostery_browser_hub_onboarding_skip": {
"message": "Skip"
},
"ghostery_browser_hub_onboarding_we_take_your_privacy_very_seriously": {
"message": "We take your privacy very seriously. Learn more"
},
"ghostery_browser_hub_onboarding_private_by_design": {
"message": "Private by design"
},
"ghostery_browser_hub_onboarding_private_by_design_description": {
"message": "Privacy is incredibly important to us at Ghostery and we've taken extra care to design our products that limits the amount of personal information collected."
},
"ghostery_browser_hub_onboarding_why_do_you_need_my_email": {
"message": "Why do you need my email?"
},
"ghostery_browser_hub_onboarding_why_do_you_need_my_email_description": {
"message": "We've found email is the simplest way to create and secure a unique account without sharing your information. It helps sync your custom settings across your devices."
},
"ghostery_browser_hub_onboarding_what_do_you_use_my_email_for": {
"message": "What do you use my email for?"
},
"ghostery_browser_hub_onboarding_what_do_you_use_my_email_for_description": {
"message": "We never share or sell your personal information. Your email address is used for account management, technical support, product-related communications, and some opt-in features."
},
"ghostery_browser_hub_onboarding_how_secure_is_": {
"message": "How secure is"
},
"ghostery_browser_hub_onboarding_how_secure_is_ghostery_description": {
"message": "We never share or sell your personal information. Your email address is used for account management, technical support, product-related communications, and some opt-in features."
},
"ghostery_browser_hub_onboarding_can_i_remove_my_account": {
"message": "Can I remove my account?"
},
"ghostery_browser_hub_onboarding_can_i_remove_my_account_description": {
"message": "We hope you enjoy using Ghostery, but if you do decide to leave we will remove all of your personal information within 90 days from the date of request."
},
"ghostery_browser_hub_onboarding_visit_our_privacy_policy": {
"message": "Visit our Privacy Policy for more information"
},
"ghostery_browser_hub_onboarding_create_an_account": {
"message": "Create an account."
},
"ghostery_browser_hub_onboarding_you_are_signed_in_as": {
"message": "You are signed in as"
},
"ghostery_browser_hub_onboarding_yay_youre_all_set": {
"message": "Yay! You're all set."
},
@@ -2087,6 +2150,9 @@
"password_colon": {
"message": "Password:"
},
"confirm_password_colon": {
"message": "Confirm Password:"
},
"please_enter_a_valid_email": {
"message": "Please enter a valid email."
},
@@ -27,7 +27,8 @@ import {
GET_USER_SETTINGS_SUCCESS,
GET_USER_SETTINGS_FAIL,
GET_USER_SUBSCRIPTION_DATA_FAIL,
GET_USER_SUBSCRIPTION_DATA_SUCCESS
GET_USER_SUBSCRIPTION_DATA_SUCCESS,
ACCOUNT_DATA_EMAIL_PREFERENCES_CHECKBOX_CHANGE
} from './AccountConstants';
import { SET_TOAST } from '../hub/Views/AppView/AppViewConstants';
import { CLEAR_THEME } from '../panel/constants/constants';
@@ -201,3 +202,10 @@ export const resetPassword = email => dispatch => (
});
})
);

export const handleEmailPreferencesCheckboxChange = (name, checked) => dispatch => (
dispatch({
type: ACCOUNT_DATA_EMAIL_PREFERENCES_CHECKBOX_CHANGE,
payload: { name, checked },
})
);
@@ -36,3 +36,6 @@ export const GET_USER_SETTINGS_FAIL = 'GET_USER_SETTINGS_FAIL';
// Update Subscription Data
export const GET_USER_SUBSCRIPTION_DATA_FAIL = 'GET_USER_SUBSCRIPTION_DATA_FAIL';
export const GET_USER_SUBSCRIPTION_DATA_SUCCESS = 'GET_USER_SUBSCRIPTION_DATA_SUCCESS';

// Opt into/out of updates and promotions sendgrid emails
export const ACCOUNT_DATA_EMAIL_PREFERENCES_CHECKBOX_CHANGE = 'ACCOUNT_DATA_EMAIL_PREFERENCES_CHECKBOX_CHANGE';
@@ -20,7 +20,8 @@ import {
GET_USER_SUBSCRIPTION_DATA_FAIL,
GET_USER_SUBSCRIPTION_DATA_SUCCESS,
RESET_PASSWORD_SUCCESS,
RESET_PASSWORD_FAIL
RESET_PASSWORD_FAIL,
ACCOUNT_DATA_EMAIL_PREFERENCES_CHECKBOX_CHANGE
} from './AccountConstants';
import { UPDATE_PANEL_DATA } from '../panel/constants/constants';

@@ -118,6 +119,15 @@ export default (state = initialState, action) => {
resetPasswordError: true
};
}
case ACCOUNT_DATA_EMAIL_PREFERENCES_CHECKBOX_CHANGE: {
const { name, checked } = action.payload;
let emailPreferences;
if (name === 'global') {
emailPreferences = { ...state.user.emailPreferences, ...{ global: checked } };
}
const user = { ...state.user, ...{ emailPreferences } };
return { ...state, ...{ user } };
}

default: return state;
}
@@ -0,0 +1,256 @@
/**
* Browser Create Account Form
*
* Ghostery Browser Extension
* https://www.ghostery.com/
*
* Copyright 2019 Ghostery, Inc. All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/

import React, { useRef, useState } from 'react';
import PropTypes from 'prop-types';
import ClassNames from 'classnames';
import { NavLink } from 'react-router-dom';
import { ToggleCheckbox } from '../../../shared-components';

const promoString = `${t('ghostery_browser_hub_onboarding_send_me')} Ghostery ${t('ghostery_browser_hub_onboarding_updates_and_promotions')}`;

/**
* A Functional React component for rendering the Browser Create Account View
* @return {JSX} JSX for rendering the Browser Create Account View of the Hub app
* @memberof HubComponents
*/
const Step1_CreateAccountForm = (props) => {
const {
email,
emailError,
confirmEmail,
confirmEmailError,
firstName,
lastName,
password,
passwordInvalidError,
passwordLengthError,
confirmPassword,
confirmPasswordError,
legalConsentChecked,
legalConsentNotCheckedError,
handleLegalConsentCheckboxChange,
isUpdatesChecked,
handleInputChange,
handleUpdatesCheckboxChange,
handleSubmit,
} = props;

const emailInputClassNames = ClassNames('Step1_CreateAccountForm__inputBox', {
error: emailError,
});
const confirmInputClassNames = ClassNames('Step1_CreateAccountForm__inputBox', {
error: confirmEmailError,
});
const passwordInputClassNames = ClassNames('Step1_CreateAccountForm__inputBox', {
error: passwordInvalidError || passwordLengthError,
});
const legalConsentClassNames = ClassNames('Step1_CreateAccountForm__legalConsentCheckedLabel', {
error: legalConsentNotCheckedError
});

return (
<form onSubmit={handleSubmit}>
<div className="Step1_CreateAccountForm--addPaddingTop row align-center-middle">
<div className="columns small-10 medium-5">
<label htmlFor="create-account-email" className="Step1_CreateAccountForm__inputLabel">
{t('email_colon')}
</label>
<input
id="create-account-email"
className={emailInputClassNames}
name="email"
type="text"
value={email}
onChange={handleInputChange}
pattern=".{1,}"
autoComplete="off"
placeholder="example@mail.com"
/>
{emailError && (
<div className="Step1_CreateAccountForm__inputErrorContainer">
<div className="Step1_CreateAccountForm__inputError">
{t('please_enter_a_valid_email')}
</div>
</div>
)}
</div>
<div className="columns small-10 medium-5">
<label htmlFor="create-account-confirmEmail" className="Step1_CreateAccountForm__inputLabel">
{t('confirm_email_colon')}
</label>
<input
id="create-account-confirmEmail"
className={confirmInputClassNames}
name="confirmEmail"
type="text"
value={confirmEmail}
onChange={handleInputChange}
pattern=".{1,}"
autoComplete="off"
/>
{confirmEmailError && (
<div className="Step1_CreateAccountForm__inputErrorContainer">
<div className="Step1_CreateAccountForm__inputError">
{t('your_email_do_not_match')}
</div>
</div>
)}
</div>
</div>
<div className="row align-center-middle">
<div className="columns small-10 medium-5">
<label htmlFor="create-account-firstName" className="Step1_CreateAccountForm__inputLabel">
{t('hub_create_account_label_first_name')}
</label>
<input
id="create-account-firstName"
className="Step1_CreateAccountForm__inputBox"
name="firstName"
type="text"
value={firstName}
onChange={handleInputChange}
pattern=".{1,}"
autoComplete="off"
/>
</div>
<div className="columns small-10 medium-5">
<label htmlFor="create-account-lastName" className="Step1_CreateAccountForm__inputLabel">
{t('hub_create_account_label_email_last_name')}
</label>
<input
id="create-account-lastName"
className="Step1_CreateAccountForm__inputBox"
name="lastName"
type="text"
value={lastName}
onChange={handleInputChange}
pattern=".{1,}"
autoComplete="off"
/>
</div>
</div>
<div className="row align-center-middle">
<div className="columns small-10 medium-5">
<label htmlFor="create-account-password" className="Step1_CreateAccountForm__inputLabel">
{t('password_colon')}
</label>
<input
id="create-account-password"
className={passwordInputClassNames}
name="password"
type="password"
value={password}
onChange={handleInputChange}
pattern=".{1,}"
autoComplete="off"
/>
{passwordInvalidError && (
<div className="Step1_CreateAccountForm__inputErrorContainer">
<div className="Step1_CreateAccountForm__inputError">
{t('hub_create_account_label_password_invalid')}
</div>
</div>
)}
{passwordLengthError && (
<div className="Step1_CreateAccountForm__inputErrorContainer">
<div className="Step1_CreateAccountForm__inputError">
{t('hub_create_account_label_password_invalid_length')}
</div>
</div>
)}
</div>
<div className="columns small-10 medium-5">
<label htmlFor="create-account-password" className="Step1_CreateAccountForm__inputLabel">
{t('confirm_password_colon')}
</label>
<input
id="create-account-password"
className={passwordInputClassNames}
name="confirmPassword"
type="password"
value={confirmPassword}
onChange={handleInputChange}
pattern=".{1,}"
autoComplete="off"
/>
{confirmPasswordError && (
<div className="Step1_CreateAccountForm__inputErrorContainer">
<div className="Step1_CreateAccountForm__inputError">
{t('hub_create_account_confirm_password_do_not_match')}
</div>
</div>
)}
</div>
</div>
<div className="row align-center-middle">
<div className="columns small-10 medium-8">
<div className="Step1_CreateAccountForm__checkboxContainer Step1_CreateAccountForm--marginBottom flex-container">
<ToggleCheckbox
checked={isUpdatesChecked}
className="ToggleCheckbox--flush-left"
onChange={handleUpdatesCheckboxChange}
/>
<span
className="Step1_CreateAccountForm__promoString"
onClick={handleUpdatesCheckboxChange}
dangerouslySetInnerHTML={{ __html: promoString }}
/>
</div>
</div>
<div className="columns small-10 medium-2" />
</div>
<div className="row align-center-middle">
<div className="columns small-10 medium-8">
<div className="Step1_CreateAccountForm__checkboxContainer BrowserCreateAccountForm--marginBottom flex-container">
<ToggleCheckbox
name="globals"
checked={legalConsentChecked}
className="ToggleCheckbox--flush-left"
onChange={handleLegalConsentCheckboxChange}
/>
<span
className={legalConsentClassNames}
onClick={handleLegalConsentCheckboxChange}
dangerouslySetInnerHTML={{ __html: t('create_account_form_legal_consent_checkbox_label') }}
/>
</div>
</div>
<div className="columns small-10 medium-2" />
</div>
<div className="Step1_CreateAccountForm__ctaButtonContainer">
<button type="submit" className="Step1_CreateAccountForm__ctaButton">{t('create_account')}</button>
</div>
</form>
);
};

// PropTypes ensure we pass required props of the correct type
Step1_CreateAccountForm.propTypes = {
email: PropTypes.string.isRequired,
emailError: PropTypes.bool.isRequired,
confirmEmail: PropTypes.string.isRequired,
confirmEmailError: PropTypes.bool.isRequired,
firstName: PropTypes.string.isRequired,
lastName: PropTypes.string.isRequired,
isUpdatesChecked: PropTypes.bool.isRequired,
password: PropTypes.string.isRequired,
confirmPassword: PropTypes.string.isRequired,
passwordInvalidError: PropTypes.bool.isRequired,
passwordLengthError: PropTypes.bool.isRequired,
handleInputChange: PropTypes.func.isRequired,
handleUpdatesCheckboxChange: PropTypes.func.isRequired,
handleSubmit: PropTypes.func.isRequired,
};

export default Step1_CreateAccountForm;
ProTip! Use n and p to navigate between commits in a pull request.