From ba5d040c03a7287f1c578d421cf076d6f71be811 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 5 Mar 2020 11:22:11 -0500 Subject: [PATCH 01/13] Add ForgotPassword view to hub. Move related code from panel to shared folder --- _locales/en/messages.json | 2 +- app/Account/AccountReducer.js | 31 +++- .../CreateAccountViewContainer.jsx | 2 +- .../ForgotPasswordView/ForgotPasswordView.jsx | 102 +++++++++++ .../ForgotPasswordView.scss | 172 ++++++++++++++++++ .../ForgotPasswordViewContainer.jsx | 103 +++++++++++ app/hub/Views/ForgotPasswordView/index.js | 47 +++++ app/hub/Views/LogInView/LogInView.jsx | 7 + .../Views/LogInView/LogInViewContainer.jsx | 2 +- .../__snapshots__/LogInView.test.jsx.snap | 30 +++ app/hub/index.jsx | 2 + app/panel/components/CreateAccount.jsx | 2 +- app/panel/components/ForgotPassword.jsx | 2 +- app/panel/components/Login.jsx | 4 +- app/panel/utils/utils.js | 55 ------ app/scss/hub.scss | 1 + src/utils/utils.js | 55 ++++++ 17 files changed, 556 insertions(+), 63 deletions(-) create mode 100644 app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx create mode 100644 app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss create mode 100644 app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx create mode 100644 app/hub/Views/ForgotPasswordView/index.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4a850e047..4e2f9e2db 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -505,7 +505,7 @@ } } }, - "panel_forgot_password": { + "forgot_password": { "message": "Forgot Password?" }, "panel_help_panel_header": { diff --git a/app/Account/AccountReducer.js b/app/Account/AccountReducer.js index 148070774..48076f6b0 100644 --- a/app/Account/AccountReducer.js +++ b/app/Account/AccountReducer.js @@ -18,7 +18,9 @@ import { GET_USER_SUCCESS, GET_USER_SETTINGS_SUCCESS, GET_USER_SUBSCRIPTION_DATA_FAIL, - GET_USER_SUBSCRIPTION_DATA_SUCCESS + GET_USER_SUBSCRIPTION_DATA_SUCCESS, + RESET_PASSWORD_SUCCESS, + RESET_PASSWORD_FAIL } from './AccountConstants'; import { UPDATE_PANEL_DATA } from '../panel/constants/constants'; @@ -28,6 +30,8 @@ const initialState = { user: null, userSettings: null, subscriptionData: null, + toastMessage: '', + resetPasswordError: false }; export default (state = initialState, action) => { @@ -84,6 +88,31 @@ export default (state = initialState, action) => { subscriptionData }); } + case RESET_PASSWORD_SUCCESS: { + const toastMessage = t('banner_check_your_email_title'); + return Object.assign({}, state, { + toastMessage, + resetPasswordError: false + }); + } + case RESET_PASSWORD_FAIL: { + const { errors } = action.payload; + let errorText = t('server_error_message'); + errors.forEach((err) => { + switch (err.code) { + case '10050': + case '10110': + errorText = t('banner_email_not_in_system_message'); + break; + default: + errorText = t('server_error_message'); + } + }); + return Object.assign({}, state, { + toastMessage: errorText, + resetPasswordError: true + }); + } default: return state; } diff --git a/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx b/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx index f0099d3b2..cee68b7e4 100644 --- a/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx +++ b/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx @@ -18,7 +18,7 @@ import { validatePassword, validateEmailsMatch, validateConfirmEmail -} from '../../../panel/utils/utils'; +} from '../../../../src/utils/utils'; import CreateAccountView from './CreateAccountView'; import SignedInView from '../SignedInView'; diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx new file mode 100644 index 000000000..941546ac8 --- /dev/null +++ b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx @@ -0,0 +1,102 @@ +/** + * Forgot Password View + * + * 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 from 'react'; +import PropTypes from 'prop-types'; +import { NavLink } from 'react-router-dom'; +import ClassNames from 'classnames'; +/** + * @class Implement Forgot Password view which opens from the link on log-in page in the intro hub. + * @memberof HubComponents + */ +const ForgotPasswordView = (props) => { + const { + email, + emailError, + handleInputChange, + handleSubmit, + loading + } = props; + const emailLabelClassNames = ClassNames('ForgotPasswordView__inputLabel', { + error: emailError + }); + const emailInputClassNames = ClassNames('ForgotPasswordView__inputBox', { + error: emailError + }); + const emailAsteriskClassNames = ClassNames('ForgotPasswordView__asterisk', { + error: emailError + }); + const buttonClasses = ClassNames('ForgotPasswordView__button button success', { loading }); + return ( +
+
+
+
+
+

+ {t('forgot_password_message')} +

+
+
+
+
+
+
+
+ + + {props.emailError && ( +
+ {t('invalid_email_forgot')} +
+ )} +
+
+

+ + {t('button_cancel')} + +

+
+ +
+
+
+
+
+ ); +}; + +// PropTypes ensure we pass required props of the correct type +ForgotPasswordView.propTypes = { + email: PropTypes.string.isRequired, + emailError: PropTypes.bool.isRequired, + loading: PropTypes.bool.isRequired, +}; + +export default ForgotPasswordView; diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss new file mode 100644 index 000000000..2800ad3f6 --- /dev/null +++ b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss @@ -0,0 +1,172 @@ +/** + * Log In View Sass + * + * 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 + */ + +// Log In View +.ForgotPasswordView { + margin-top: 40px; + padding-bottom: 40px; +} +.ForgotPasswordView--addPaddingTop { + padding-top: 40px; +} +.ForgotPasswordView__headerImage { + max-width: 180px; +} +.ForgotPasswordView__headerTitle h3 { + font-size: 20px; + line-height: 1.5; + color: #4a4a4a; + margin: 20px 25px 0; + width: 500px; +} +.ForgotPasswordView__inputLabel { + font-size: 14px; + font-weight: 500; + line-height: 40px; + color: #4a4a4a; + &.error { + color: #e74055; + } +} +.ForgotPasswordView__asterisk { + display: none; + &.error { + display: inline; + color: #e74055; + } +} +.ForgotPasswordView__inputBox { + font-size: 14; + line-height: 24px; + color: #4a4a4a; + margin-bottom: 35px; + width: 456px; + + // Foundation Overrides + border-radius: 0; + box-shadow: none; + border: 1px solid #c8c7c2; +} +.ForgotPasswordView__inputBox.error { + margin-bottom: 8px; + border-color: #e74055; +} +.ForgotPasswordView__inputBox:focus { + // Foundation Overrides + box-shadow: none; + border-color: #4a4a4a; +} +.ForgotPasswordView__inputError { + font-size: 12; + line-height: 14px; + color: #e74055; + margin-bottom: 13px; + font-style: italic; +} +.ForgotPasswordView__link { + font-size: 14px; + line-height: 30px; + margin: 4px 0 0 40px; +} +.ForgotPasswordView__button { + min-width: 180px; + .loader { + display: none; + } + &.loading { + pointer-events: none; + .loader { + display: inline-block; + height: 12px; + width: 12px; + } + .title { + display: none; + } + } + &:focus { + outline: 0; + + .loader:after { + background: #760176; + } + } +} +.loader { + font-size: 10px; + text-indent: -9999em; + width: 10rem; + height: 10rem; + border-radius: 50%; + background: #ffffff; + background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + position: relative; + -webkit-animation: load3 1.4s infinite linear; + animation: load3 1.4s infinite linear; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} +.loader:before { + width: 50%; + height: 50%; + background: #ffffff; + border-radius: 100% 0 0 0; + position: absolute; + top: 0; + left: 0; + content: ''; +} +.loader:after { + background: #930194; + width: 75%; + height: 75%; + border-radius: 50%; + content: ''; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +@-webkit-keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@media only screen and (max-width: 740px) { + .ForgotPasswordView__header { + flex-direction: column; + } +} diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx b/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx new file mode 100644 index 000000000..da3cd5f24 --- /dev/null +++ b/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx @@ -0,0 +1,103 @@ +/** + * Create Account View Container + * + * 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, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { validateEmail } from '../../../../src/utils/utils'; +import ForgotPasswordView from './ForgotPasswordView'; + +/** + * @class Implement the Forgot Password View for the Ghostery Hub + * @extends Component + * @memberof HubContainers + */ +class ForgotPasswordViewContainer extends Component { + constructor(props) { + super(props); + this.state = { + email: '', + emailError: false, + loading: false, + }; + } + + /** + * Update input values by updating state. + * @param {Object} event the 'change' event + */ + handleInputChange = (event) => { + const { name, value } = event.target; + this.setState({ [name]: value }); + } + + /** + * Forgot Password Submit button + * @param {String} email the account email who's password should be reset + */ + handleSubmit = (e) => { + e.preventDefault(); + this.setState({ loading: true }, () => { + const { email } = this.state; + + // validate the email and password + if (!validateEmail(email)) { + this.setState({ + emailError: true, + loading: false, + }); + return; + } + + // Try to reset the password and display a success/error message + this.props.actions.resetPassword(email) + .then((success) => { + this.setState({ loading: false }); + if (success) { + this.props.history.push('/log-in'); + } + this.props.actions.setToast({ + toastMessage: this.props.toastMessage, + toastClass: this.props.resetPasswordError ? 'alert' : 'success', + }); + }); + }); + } + + /** + * React's required render function. Returns JSX + * @return {JSX} JSX for rendering the Create Account View of the Hub app + */ + render() { + const { email, emailError, loading } = this.state; + const childProps = { + email, + emailError, + loading, + handleInputChange: this.handleInputChange, + handleSubmit: this.handleSubmit + }; + return ( + + ); + } +} + +// PropTypes ensure we pass required props of the correct type +ForgotPasswordViewContainer.propTypes = { + actions: PropTypes.shape({ + setToast: PropTypes.func.isRequired, + resetPassword: PropTypes.func.isRequired, + }).isRequired, +}; + +export default ForgotPasswordViewContainer; diff --git a/app/hub/Views/ForgotPasswordView/index.js b/app/hub/Views/ForgotPasswordView/index.js new file mode 100644 index 000000000..dd1cadd46 --- /dev/null +++ b/app/hub/Views/ForgotPasswordView/index.js @@ -0,0 +1,47 @@ +/** + * Point of entry index.js file for Forgot Password View + * + * 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 { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import ForgotPasswordViewContainer from './ForgotPasswordViewContainer'; +import AccountReducer from '../../../Account/AccountReducer'; +import { setToast } from '../AppView/AppViewActions'; +import { resetPassword } from '../../../Account/AccountActions'; + +/** + * Map redux store state properties to the component's own properties. + * @param {Object} state entire Redux store's state + * @return {function} this function returns a plain object, which will be merged into the component's props + * @memberof HubContainers + */ +const mapStateToProps = state => Object.assign({}, state.account); + +/** + * Bind the component's action creators using Redux's bindActionCreators. + * @param {function} dispatch redux store method which dispatches actions + * @return {function} to be used as an argument in redux connect call + * @memberof SetupContainers + */ + +// const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign(actions, { resetPassword }), dispatch) }); +const mapDispatchToProps = dispatch => ({ + actions: bindActionCreators(Object.assign({}, { + setToast, + resetPassword, + }), dispatch), +}); + +export const reducer = AccountReducer; + +export default connect(mapStateToProps, mapDispatchToProps)(ForgotPasswordViewContainer); diff --git a/app/hub/Views/LogInView/LogInView.jsx b/app/hub/Views/LogInView/LogInView.jsx index 6e76b3462..6e1cba87d 100644 --- a/app/hub/Views/LogInView/LogInView.jsx +++ b/app/hub/Views/LogInView/LogInView.jsx @@ -90,6 +90,13 @@ const LogInView = (props) => { {t('hub_login_label_password_invalid')} )} +
+ + + { t('forgot_password') } + + +
{ t('hub_login_link_dont_have_account') } diff --git a/app/hub/Views/LogInView/LogInViewContainer.jsx b/app/hub/Views/LogInView/LogInViewContainer.jsx index fb9cd1289..48e58fcbf 100644 --- a/app/hub/Views/LogInView/LogInViewContainer.jsx +++ b/app/hub/Views/LogInView/LogInViewContainer.jsx @@ -13,7 +13,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { validateEmail } from '../../../panel/utils/utils'; +import { validateEmail } from '../../../../src/utils/utils'; import LogInView from './LogInView'; import SignedInView from '../SignedInView'; /** diff --git a/app/hub/Views/LogInView/__tests__/__snapshots__/LogInView.test.jsx.snap b/app/hub/Views/LogInView/__tests__/__snapshots__/LogInView.test.jsx.snap index d6b6f0e0f..0c28a94c9 100644 --- a/app/hub/Views/LogInView/__tests__/__snapshots__/LogInView.test.jsx.snap +++ b/app/hub/Views/LogInView/__tests__/__snapshots__/LogInView.test.jsx.snap @@ -67,6 +67,21 @@ exports[`app/hub/Views/LogIn component Snapshot tests with react-test-renderer l type="password" value="examplePassword" /> +
+ + + forgot_password + + +
@@ -177,6 +192,21 @@ exports[`app/hub/Views/LogIn component Snapshot tests with react-test-renderer l > hub_login_label_password_invalid
+
+ + + forgot_password + + +
diff --git a/app/hub/index.jsx b/app/hub/index.jsx index 202a0db33..1c053cba1 100644 --- a/app/hub/index.jsx +++ b/app/hub/index.jsx @@ -28,6 +28,7 @@ import PlusView from './Views/PlusView'; import RewardsView from './Views/RewardsView'; import ProductsView from './Views/ProductsView'; import CreateAccountView from './Views/CreateAccountView'; +import ForgotPasswordView from './Views/ForgotPasswordView'; import LogInView from './Views/LogInView'; const store = createStore(); @@ -45,6 +46,7 @@ const Hub = () => ( + ); diff --git a/app/panel/components/CreateAccount.jsx b/app/panel/components/CreateAccount.jsx index 7d0120504..93bc85c38 100644 --- a/app/panel/components/CreateAccount.jsx +++ b/app/panel/components/CreateAccount.jsx @@ -15,7 +15,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import ClassNames from 'classnames'; import RSVP from 'rsvp'; -import { validateEmail, validateConfirmEmail, validatePassword } from '../utils/utils'; +import { validateEmail, validateConfirmEmail, validatePassword } from '../../../src/utils/utils'; import I18nWithLink from '../../shared-components/I18nWithLink'; /** diff --git a/app/panel/components/ForgotPassword.jsx b/app/panel/components/ForgotPassword.jsx index b1d0f7bbe..c8f47bb2e 100644 --- a/app/panel/components/ForgotPassword.jsx +++ b/app/panel/components/ForgotPassword.jsx @@ -14,7 +14,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import ClassNames from 'classnames'; -import { validateEmail } from '../utils/utils'; +import { validateEmail } from '../../../src/utils/utils'; /** * @class Implement Forgot Password view which opens from the link on Sign In panel. * @memberof PanelClasses diff --git a/app/panel/components/Login.jsx b/app/panel/components/Login.jsx index 6d793515c..195cc36d0 100644 --- a/app/panel/components/Login.jsx +++ b/app/panel/components/Login.jsx @@ -15,7 +15,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import ClassNames from 'classnames'; import RSVP from 'rsvp'; -import { validateEmail } from '../utils/utils'; +import { validateEmail } from '../../../src/utils/utils'; import { log } from '../../../src/utils/common'; import history from '../utils/history'; @@ -137,7 +137,7 @@ class Login extends React.Component {
- { t('panel_forgot_password') } + { t('forgot_password') }
{ t('create_account') } diff --git a/app/panel/utils/utils.js b/app/panel/utils/utils.js index a92dde970..1d519bcb1 100644 --- a/app/panel/utils/utils.js +++ b/app/panel/utils/utils.js @@ -110,61 +110,6 @@ export function computeTimeDelta(start, end) { }; } -/** - * Check for valid email - * @memberOf PanelUtils - * @param {string} email email to validate - * @return {boolean} true if valid, false otherwise - */ -export function validateEmail(email) { - // eslint-disable-next-line no-useless-escape, no-control-regex - const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i; - return email !== '' && emailRegex.test(email); -} - -/** - * Check for valid confirm email and equality to email - * @memberOf PanelUtils - * @param {string} email email to validate - * @param {string} confirmEmail confirm email to validate - * @return {boolean} true if valid, false otherwise - */ -export function validateConfirmEmail(email, confirmEmail) { - if (!email || !confirmEmail) { - return false; - } - const lEmail = email.toLowerCase(); - const lConfirmEmail = confirmEmail.toLowerCase(); - return validateEmail(confirmEmail) && (lEmail === lConfirmEmail) || false; -} - -/** - * Check for confirm email equality to email - * @memberOf PanelUtils - * @param {string} email email - * @param {string} confirmEmail confirm email to validate - * @return {boolean} true if equal, false otherwise - */ -export function validateEmailsMatch(email, confirmEmail) { - if (!email || !confirmEmail) { - return false; - } - const lEmail = email.toLowerCase(); - const lConfirmEmail = confirmEmail.toLowerCase(); - return lEmail === lConfirmEmail; -} - -/** - * Check for valid password - * @memberOf PanelUtils - * @param {string} pwd password to validate - * @return {boolean} true if valid, false otherwise - */ -export function validatePassword(pwd) { - const pwdRegex = /^[a-zA-Z0-9!@#$%^&*=+()<>{}[\];:,./?]{8,50}$/; - return pwd !== '' && pwdRegex.test(pwd); -} - /** * Helper method for making XHR requests * @memberOf PanelUtils diff --git a/app/scss/hub.scss b/app/scss/hub.scss index 9cc1a696b..d562f9b3e 100644 --- a/app/scss/hub.scss +++ b/app/scss/hub.scss @@ -71,6 +71,7 @@ html, body, #root { @import '../hub/Views/ProductsView/ProductsView.scss'; @import '../hub/Views/SignedInView/SignedInView.scss'; @import '../hub/Views/LogInView/LogInView.scss'; +@import '../hub/Views/ForgotPasswordView/ForgotPasswordView.scss'; @import '../hub/Views/CreateAccountView/CreateAccountView.scss'; // Imports from ../shared-components directory diff --git a/src/utils/utils.js b/src/utils/utils.js index e4f261523..37f75cf63 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -617,3 +617,58 @@ export function injectNotifications(tab_id, importExport = false) { export function isCliqzOffer(offer) { return (offer && offer.origin === 'cliqz' && offer.type === 'offers' && offer.data); } + +/** + * Check for valid email + * @memberOf PanelUtils + * @param {string} email email to validate + * @return {boolean} true if valid, false otherwise + */ +export function validateEmail(email) { + // eslint-disable-next-line no-useless-escape, no-control-regex + const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i; + return email !== '' && emailRegex.test(email); +} + +/** + * Check for valid confirm email and equality to email + * @memberOf PanelUtils + * @param {string} email email to validate + * @param {string} confirmEmail confirm email to validate + * @return {boolean} true if valid, false otherwise + */ +export function validateConfirmEmail(email, confirmEmail) { + if (!email || !confirmEmail) { + return false; + } + const lEmail = email.toLowerCase(); + const lConfirmEmail = confirmEmail.toLowerCase(); + return validateEmail(confirmEmail) && (lEmail === lConfirmEmail) || false; +} + +/** + * Check for confirm email equality to email + * @memberOf PanelUtils + * @param {string} email email + * @param {string} confirmEmail confirm email to validate + * @return {boolean} true if equal, false otherwise + */ +export function validateEmailsMatch(email, confirmEmail) { + if (!email || !confirmEmail) { + return false; + } + const lEmail = email.toLowerCase(); + const lConfirmEmail = confirmEmail.toLowerCase(); + return lEmail === lConfirmEmail; +} + +/** + * Check for valid password + * @memberOf PanelUtils + * @param {string} pwd password to validate + * @return {boolean} true if valid, false otherwise + */ +export function validatePassword(pwd) { + const pwdRegex = /^[a-zA-Z0-9!@#$%^&*=+()<>{}[\];:,./?]{8,50}$/; + return pwd !== '' && pwdRegex.test(pwd); +} From 6474c4acfb29c225711eedd21ca9d9fa60bea901 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 5 Mar 2020 11:34:03 -0500 Subject: [PATCH 02/13] Remove unused scss --- .../Views/ForgotPasswordView/ForgotPasswordView.scss | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss index 2800ad3f6..67fa4d30f 100644 --- a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss +++ b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss @@ -19,16 +19,6 @@ .ForgotPasswordView--addPaddingTop { padding-top: 40px; } -.ForgotPasswordView__headerImage { - max-width: 180px; -} -.ForgotPasswordView__headerTitle h3 { - font-size: 20px; - line-height: 1.5; - color: #4a4a4a; - margin: 20px 25px 0; - width: 500px; -} .ForgotPasswordView__inputLabel { font-size: 14px; font-weight: 500; From 1cbf85ceb828cdcc61290b063fc07e9ead73c448 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 5 Mar 2020 11:40:17 -0500 Subject: [PATCH 03/13] Update and remove comments --- app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss | 3 +-- .../Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx | 2 +- app/hub/Views/ForgotPasswordView/index.js | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss index 67fa4d30f..d0f21b86d 100644 --- a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss +++ b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss @@ -1,5 +1,5 @@ /** - * Log In View Sass + * ForgotPasswordView Sass * * Ghostery Browser Extension * https://www.ghostery.com/ @@ -11,7 +11,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ -// Log In View .ForgotPasswordView { margin-top: 40px; padding-bottom: 40px; diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx b/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx index da3cd5f24..c7bb2ac47 100644 --- a/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx +++ b/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx @@ -1,5 +1,5 @@ /** - * Create Account View Container + * Forgot Password View Container * * Ghostery Browser Extension * https://www.ghostery.com/ diff --git a/app/hub/Views/ForgotPasswordView/index.js b/app/hub/Views/ForgotPasswordView/index.js index dd1cadd46..ab76a30c4 100644 --- a/app/hub/Views/ForgotPasswordView/index.js +++ b/app/hub/Views/ForgotPasswordView/index.js @@ -34,7 +34,6 @@ const mapStateToProps = state => Object.assign({}, state.account); * @memberof SetupContainers */ -// const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign(actions, { resetPassword }), dispatch) }); const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign({}, { setToast, From c97b10c1ac18217ab275428fff5348f4b8541767 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 7 Apr 2020 17:15:00 -0400 Subject: [PATCH 04/13] Move ForgotPassword to shared-components --- .../ForgotPassword/ForgotPassword.jsx | 118 ++++++++++++++++++ .../ForgotPassword/ForgotPassword.scss | 31 +++++ .../ForgotPassword/ForgotPasswordContainer.js | 45 +++++++ app/shared-components/ForgotPassword/index.js | 17 +++ 4 files changed, 211 insertions(+) create mode 100644 app/shared-components/ForgotPassword/ForgotPassword.jsx create mode 100644 app/shared-components/ForgotPassword/ForgotPassword.scss create mode 100644 app/shared-components/ForgotPassword/ForgotPasswordContainer.js create mode 100644 app/shared-components/ForgotPassword/index.js diff --git a/app/shared-components/ForgotPassword/ForgotPassword.jsx b/app/shared-components/ForgotPassword/ForgotPassword.jsx new file mode 100644 index 000000000..c8f47bb2e --- /dev/null +++ b/app/shared-components/ForgotPassword/ForgotPassword.jsx @@ -0,0 +1,118 @@ +/** + * Forgot Password Component + * + * 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 from 'react'; +import { Link } from 'react-router-dom'; +import ClassNames from 'classnames'; +import { validateEmail } from '../../../src/utils/utils'; +/** + * @class Implement Forgot Password view which opens from the link on Sign In panel. + * @memberof PanelClasses + */ +class ForgotPassword extends React.Component { + constructor(props) { + super(props); + this.state = { + email: '', + loading: false, + emailError: false, + }; + } + + /** + * Update state with changed values. + * @param {Object} event 'change' event + */ + handleInputChange = (e) => { + const { name, value } = e.target; + this.setState({ [name]: value }); + } + + /** + * Validate entered data, notify user if validation fails, + * This action is one the PanelActions. + */ + handleSubmit = (e) => { + e.preventDefault(); + this.setState({ loading: true }, () => { + const { email } = this.state; + + // validate the email and password + if (!validateEmail(email)) { + this.setState({ + emailError: true, + loading: false, + }); + return; + } + + this.props.actions.resetPassword(email) + .then((success) => { + this.setState({ loading: false }); + if (success) { + this.props.history.push('/login'); + } + }); + }); + } + + /** + * Render Forgot Password panel. + * @return {ReactComponent} ReactComponent instance + */ + render() { + const { email, loading, emailError } = this.state; + const buttonClasses = ClassNames('button ghostery-button', { loading }); + return ( +
+
+
+
+

+ { t('forgot_password_message') } +

+
+ +

+ { t('invalid_email_forgot') } +

+

+ { t('error_email_forgot') } +

+
+
+
+ + { t('button_cancel') } + +
+
+ +
+
+
+
+
+
+ ); + } +} + +export default ForgotPassword; diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss new file mode 100644 index 000000000..54749a899 --- /dev/null +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -0,0 +1,31 @@ +/* FORGOT PASSWORD PANEL */ +#forgot-password-panel{ + margin-top: 75px; + h4#forgot-password-message { + font-size:14px; + font-weight: normal; + color: #333333; + margin-bottom: 50px; + } + #forgot-email { + &.not-found-error { + p.warning.invalid-email { + opacity: 0; + } + } + &.invalid-email { + p.warning.not-found-error { + opacity: 0; + } + } + } + .buttons-container { + margin-top: 60px; + #send-button { + width:126px; + } + } +} + +/* FORGOT PASSWORD HUB */ + diff --git a/app/shared-components/ForgotPassword/ForgotPasswordContainer.js b/app/shared-components/ForgotPassword/ForgotPasswordContainer.js new file mode 100644 index 000000000..098f93bbe --- /dev/null +++ b/app/shared-components/ForgotPassword/ForgotPasswordContainer.js @@ -0,0 +1,45 @@ +/** + * Forgot Password Container + * + * 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 { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import ForgotPassword from './ForgotPassword'; +import * as actions from '../../panel/actions/PanelActions'; // get shared actions from Panel +import { resetPassword } from '../../Account/AccountActions'; +/** + * Map redux store state properties to ForgotPassword component own properties. + * @memberOf PanelContainers + * @param {Object} state entire Redux store's state + * @param {Object} ownProps props passed to the connected component + * @return {function} this function returns plain object, which will be merged into ForgotPassword component props + * @todo We are not using ownProps, so we better not specify it explicitly, + * in this case it won't be passed by React (see https://github.com/reactjs/react-redux/blob/master/docs/api.md). + */ +const mapStateToProps = () => Object.assign({}); +/** + * Bind ForgotPassword component action creators using Redux's bindActionCreators + * @memberOf PanelContainers + * @param {function} dispatch redux store method which dispatches actions + * @param {Object} ownProps ForgotPassword component own props + * @return {function} to be used as an argument in redux connect call + */ +const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign(actions, { resetPassword }), dispatch) }); +/** + * Connect ForgotPassword component to the Redux store. + * @memberOf PanelContainers + * @param {function} mapStateToProps maps redux store state properties to ForgotPassword component own properties + * @param {function} mapDispatchToProps binds ForgotPassword component action creators + * @return {Object} A higher-order React component class that passes state and action + * creators into ForgotPassword component. Used by React framework. + */ +export default connect(mapStateToProps, mapDispatchToProps)(ForgotPassword); diff --git a/app/shared-components/ForgotPassword/index.js b/app/shared-components/ForgotPassword/index.js new file mode 100644 index 000000000..73361bd8c --- /dev/null +++ b/app/shared-components/ForgotPassword/index.js @@ -0,0 +1,17 @@ +/** + * Point of entry index.js file for ForgotPassword + * + * + * 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 ForgotPassword from './ForgotPassword'; + +export default ForgotPassword; From 2e1a3f7f1b73c116e43c03fe9f1c452c2f9a3a0f Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 9 Apr 2020 03:49:02 -0400 Subject: [PATCH 05/13] Hook up redux. Start merging panel and hub jsx and scss --- app/hub/index.jsx | 4 +- .../containers/ForgotPasswordContainer.js | 45 ----- app/panel/index.jsx | 4 +- app/scss/hub.scss | 1 + app/scss/panel.scss | 1 + .../ForgotPassword/ForgotPassword.jsx | 32 +++- .../ForgotPassword/ForgotPassword.scss | 167 ++++++++++++++++++ app/shared-components/ForgotPassword/index.js | 17 -- app/shared-components/index.js | 4 +- 9 files changed, 202 insertions(+), 73 deletions(-) delete mode 100644 app/panel/containers/ForgotPasswordContainer.js delete mode 100644 app/shared-components/ForgotPassword/index.js diff --git a/app/hub/index.jsx b/app/hub/index.jsx index 1c053cba1..20c7067cd 100644 --- a/app/hub/index.jsx +++ b/app/hub/index.jsx @@ -28,7 +28,7 @@ import PlusView from './Views/PlusView'; import RewardsView from './Views/RewardsView'; import ProductsView from './Views/ProductsView'; import CreateAccountView from './Views/CreateAccountView'; -import ForgotPasswordView from './Views/ForgotPasswordView'; +import ForgotPasswordView from '../shared-components/ForgotPassword/ForgotPasswordContainer'; import LogInView from './Views/LogInView'; const store = createStore(); @@ -46,7 +46,7 @@ const Hub = () => ( - + } /> ); diff --git a/app/panel/containers/ForgotPasswordContainer.js b/app/panel/containers/ForgotPasswordContainer.js deleted file mode 100644 index 92e71accc..000000000 --- a/app/panel/containers/ForgotPasswordContainer.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Forgot Password Container - * - * 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 { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import ForgotPassword from '../components/ForgotPassword'; -import * as actions from '../actions/PanelActions'; // get shared actions from Panel -import { resetPassword } from '../../Account/AccountActions'; -/** - * Map redux store state properties to ForgotPassword component own properties. - * @memberOf PanelContainers - * @param {Object} state entire Redux store's state - * @param {Object} ownProps props passed to the connected component - * @return {function} this function returns plain object, which will be merged into ForgotPassword component props - * @todo We are not using ownProps, so we better not specify it explicitly, - * in this case it won't be passed by React (see https://github.com/reactjs/react-redux/blob/master/docs/api.md). - */ -const mapStateToProps = () => Object.assign({}); -/** - * Bind ForgotPassword component action creators using Redux's bindActionCreators - * @memberOf PanelContainers - * @param {function} dispatch redux store method which dispatches actions - * @param {Object} ownProps ForgotPassword component own props - * @return {function} to be used as an argument in redux connect call - */ -const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign(actions, { resetPassword }), dispatch) }); -/** - * Connect ForgotPassword component to the Redux store. - * @memberOf PanelContainers - * @param {function} mapStateToProps maps redux store state properties to ForgotPassword component own properties - * @param {function} mapDispatchToProps binds ForgotPassword component action creators - * @return {Object} A higher-order React component class that passes state and action - * creators into ForgotPassword component. Used by React framework. - */ -export default connect(mapStateToProps, mapDispatchToProps)(ForgotPassword); diff --git a/app/panel/index.jsx b/app/panel/index.jsx index f9d258453..b32c1423c 100644 --- a/app/panel/index.jsx +++ b/app/panel/index.jsx @@ -25,7 +25,7 @@ import Settings from './containers/SettingsContainer'; import Subscription from './containers/SubscriptionContainer'; import Login from './containers/LoginContainer'; import CreateAccount from './containers/CreateAccountContainer'; -import ForgotPassword from './containers/ForgotPasswordContainer'; +import ForgotPassword from '../shared-components/ForgotPassword/ForgotPasswordContainer'; import AccountSuccess from './containers/AccountSuccessContainer'; import configureStore from './store/configureStore'; import Help from './components/Help'; @@ -51,7 +51,7 @@ const Ghostery = () => ( - + } /> ); diff --git a/app/scss/hub.scss b/app/scss/hub.scss index d562f9b3e..4a294376a 100644 --- a/app/scss/hub.scss +++ b/app/scss/hub.scss @@ -82,3 +82,4 @@ html, body, #root { @import '../shared-components/ToastMessage/ToastMessage.scss'; @import '../shared-components/ToggleCheckbox/ToggleCheckbox.scss'; @import '../shared-components/ToggleSwitch/ToggleSwitch.scss'; +@import '../shared-components/ForgotPassword/ForgotPassword.scss'; diff --git a/app/scss/panel.scss b/app/scss/panel.scss index eaef66ef7..45520a859 100644 --- a/app/scss/panel.scss +++ b/app/scss/panel.scss @@ -80,3 +80,4 @@ html body { // Imports from ../shared-components directory @import '../shared-components/Modal/Modal.scss'; @import '../shared-components/PremiumPromoModal/PremiumPromoModal.scss'; +@import '../shared-components/ForgotPassword/ForgotPassword.scss' diff --git a/app/shared-components/ForgotPassword/ForgotPassword.jsx b/app/shared-components/ForgotPassword/ForgotPassword.jsx index c8f47bb2e..85668c66c 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.jsx +++ b/app/shared-components/ForgotPassword/ForgotPassword.jsx @@ -60,7 +60,8 @@ class ForgotPassword extends React.Component { .then((success) => { this.setState({ loading: false }); if (success) { - this.props.history.push('/login'); + // this.props.history.push('/login'); + // go back to hub sign in screen } }); }); @@ -72,20 +73,39 @@ class ForgotPassword extends React.Component { */ render() { const { email, loading, emailError } = this.state; + const { location } = this.props; const buttonClasses = ClassNames('button ghostery-button', { loading }); + + const ContainerClassNames = ClassNames('', { + 'forgot-password-panel': location === 'panel', + ForgotPasswordView: location === 'hub', + }); + const MessageClassNames = ClassNames('', { + 'forgot-password-message': location === 'panel', + ForgotPasswordMessage: location === 'hub', + }); + const EmailClassNames = ClassNames('', { + 'forgot-input-email': location === 'panel', + ForgotPasswordMessage: location === 'hub', + }); + + const ButtonsContainerClassNames = ClassNames('row', { + 'buttons-container': location === 'panel', + ForgotPasswordButtonsContainer: location === 'hub', + }); return ( -
+
-

+

{ t('forgot_password_message') }

-
-
+
{ t('button_cancel') } diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss index 54749a899..ce701f637 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.scss +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -28,4 +28,171 @@ } /* FORGOT PASSWORD HUB */ +#ForgotPasswordView { + margin: 40px auto; + padding-bottom: 40px; + #ForgotPasswordMessage { + width: 456px; + } + #forgot-email { + &.not-found-error { + p.warning.invalid-email { + opacity: 0; + } + } + &.invalid-email { + p.warning.not-found-error { + opacity: 0; + } + } + } + .ForgotPasswordButtonsContainer { + width: 456px; + } +} + + +.ForgotPasswordView--addPaddingTop { + padding-top: 40px; +} +.ForgotPasswordView__inputLabel { + font-size: 14px; + font-weight: 500; + line-height: 40px; + color: #4a4a4a; + &.error { + color: #e74055; + } +} +.ForgotPasswordView__asterisk { + display: none; + &.error { + display: inline; + color: #e74055; + } +} +.ForgotPasswordView__inputBox { + font-size: 14; + line-height: 24px; + color: #4a4a4a; + margin-bottom: 35px; + width: 456px; + // Foundation Overrides + border-radius: 0; + box-shadow: none; + border: 1px solid #c8c7c2; +} +.ForgotPasswordView__inputBox.error { + margin-bottom: 8px; + border-color: #e74055; +} +.ForgotPasswordView__inputBox:focus { + // Foundation Overrides + box-shadow: none; + border-color: #4a4a4a; +} +.ForgotPasswordView__inputError { + font-size: 12; + line-height: 14px; + color: #e74055; + margin-bottom: 13px; + font-style: italic; +} +.ForgotPasswordView__link { + font-size: 14px; + line-height: 30px; + margin: 4px 0 0 40px; +} +.ForgotPasswordView__button { + min-width: 180px; + .loader { + display: none; + } + &.loading { + pointer-events: none; + .loader { + display: inline-block; + height: 12px; + width: 12px; + } + .title { + display: none; + } + } + &:focus { + outline: 0; + + .loader:after { + background: #760176; + } + } +} +.loader { + font-size: 10px; + text-indent: -9999em; + width: 10rem; + height: 10rem; + border-radius: 50%; + background: #ffffff; + background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + position: relative; + -webkit-animation: load3 1.4s infinite linear; + animation: load3 1.4s infinite linear; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} +.loader:before { + width: 50%; + height: 50%; + background: #ffffff; + border-radius: 100% 0 0 0; + position: absolute; + top: 0; + left: 0; + content: ''; +} +.loader:after { + background: #930194; + width: 75%; + height: 75%; + border-radius: 50%; + content: ''; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +@-webkit-keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@media only screen and (max-width: 740px) { + .ForgotPasswordView__header { + flex-direction: column; + } +} diff --git a/app/shared-components/ForgotPassword/index.js b/app/shared-components/ForgotPassword/index.js deleted file mode 100644 index 73361bd8c..000000000 --- a/app/shared-components/ForgotPassword/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Point of entry index.js file for ForgotPassword - * - * - * 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 ForgotPassword from './ForgotPassword'; - -export default ForgotPassword; diff --git a/app/shared-components/index.js b/app/shared-components/index.js index 8cea5f1ae..6b8e4f56e 100644 --- a/app/shared-components/index.js +++ b/app/shared-components/index.js @@ -22,6 +22,7 @@ import SteppedNavigation from './SteppedNavigation'; import ToastMessage from './ToastMessage'; import ToggleCheckbox from './ToggleCheckbox'; import ToggleSwitch from './ToggleSwitch'; +import ForgotPassword from './ForgotPassword'; export { ExitButton, @@ -30,5 +31,6 @@ export { SteppedNavigation, ToastMessage, ToggleCheckbox, - ToggleSwitch + ToggleSwitch, + ForgotPassword }; From 665c985b630411ce97355f280689a7b85c935b00 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 13 Apr 2020 12:55:26 -0400 Subject: [PATCH 06/13] Reuse panel css inside hub --- app/Account/AccountActions.js | 1 + .../ForgotPassword/ForgotPassword.jsx | 2 +- .../ForgotPassword/ForgotPassword.scss | 299 ++++++++++-------- app/shared-components/index.js | 2 +- 4 files changed, 164 insertions(+), 140 deletions(-) diff --git a/app/Account/AccountActions.js b/app/Account/AccountActions.js index a3851dd40..77563155f 100644 --- a/app/Account/AccountActions.js +++ b/app/Account/AccountActions.js @@ -166,6 +166,7 @@ export const logout = () => dispatch => ( ); export const resetPassword = email => dispatch => ( + // console.log('resetting password...'); sendMessageInPromise('account.resetPassword', { email }) .then((res) => { const { errors } = res; diff --git a/app/shared-components/ForgotPassword/ForgotPassword.jsx b/app/shared-components/ForgotPassword/ForgotPassword.jsx index 85668c66c..8bed0bf54 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.jsx +++ b/app/shared-components/ForgotPassword/ForgotPassword.jsx @@ -60,7 +60,7 @@ class ForgotPassword extends React.Component { .then((success) => { this.setState({ loading: false }); if (success) { - // this.props.history.push('/login'); + // this.props.history.push('/log-in'); // go back to hub sign in screen } }); diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss index ce701f637..def80744e 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.scss +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -51,148 +51,171 @@ } } - -.ForgotPasswordView--addPaddingTop { - padding-top: 40px; -} -.ForgotPasswordView__inputLabel { - font-size: 14px; - font-weight: 500; - line-height: 40px; - color: #4a4a4a; - &.error { - color: #e74055; - } -} -.ForgotPasswordView__asterisk { - display: none; - &.error { - display: inline; - color: #e74055; - } -} -.ForgotPasswordView__inputBox { - font-size: 14; - line-height: 24px; - color: #4a4a4a; - margin-bottom: 35px; - width: 456px; - - // Foundation Overrides - border-radius: 0; - box-shadow: none; - border: 1px solid #c8c7c2; -} -.ForgotPasswordView__inputBox.error { - margin-bottom: 8px; - border-color: #e74055; -} -.ForgotPasswordView__inputBox:focus { - // Foundation Overrides - box-shadow: none; - border-color: #4a4a4a; -} -.ForgotPasswordView__inputError { - font-size: 12; - line-height: 14px; - color: #e74055; - margin-bottom: 13px; +p.warning { + margin: 0; + font-size: 12px; + line-height: 12px; + font-weight: 400; font-style: italic; + color: #CC5F5A; + opacity: 0; + @include transition(opacity 0.2s, color 0.2s); } -.ForgotPasswordView__link { - font-size: 14px; - line-height: 30px; - margin: 4px 0 0 40px; -} -.ForgotPasswordView__button { - min-width: 180px; - .loader { - display: none; +.panel-error { + span.asterisk {display: inline;} + p.warning { + opacity: 1; } - &.loading { - pointer-events: none; - .loader { - display: inline-block; - height: 12px; - width: 12px; - } - .title { - display: none; - } + input { + border-color: #E0B4B4; + background-color: #FFF6F6; } - &:focus { - outline: 0; - - .loader:after { - background: #760176; - } + label { + color: #CC5F5A; } } -.loader { - font-size: 10px; - text-indent: -9999em; - width: 10rem; - height: 10rem; - border-radius: 50%; - background: #ffffff; - background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - position: relative; - -webkit-animation: load3 1.4s infinite linear; - animation: load3 1.4s infinite linear; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -.loader:before { - width: 50%; - height: 50%; - background: #ffffff; - border-radius: 100% 0 0 0; - position: absolute; - top: 0; - left: 0; - content: ''; -} -.loader:after { - background: #930194; - width: 75%; - height: 75%; - border-radius: 50%; - content: ''; - margin: auto; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; -} -@-webkit-keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@media only screen and (max-width: 740px) { - .ForgotPasswordView__header { - flex-direction: column; - } -} +// .ForgotPasswordView--addPaddingTop { +// padding-top: 40px; +// } +// .ForgotPasswordView__inputLabel { +// font-size: 14px; +// font-weight: 500; +// line-height: 40px; +// color: #4a4a4a; +// &.error { +// color: #e74055; +// } +// } +// .ForgotPasswordView__asterisk { +// display: none; +// &.error { +// display: inline; +// color: #e74055; +// } +// } +// .ForgotPasswordView__inputBox { +// font-size: 14; +// line-height: 24px; +// color: #4a4a4a; +// margin-bottom: 35px; +// width: 456px; + +// // Foundation Overrides +// border-radius: 0; +// box-shadow: none; +// border: 1px solid #c8c7c2; +// } +// .ForgotPasswordView__inputBox.error { +// margin-bottom: 8px; +// border-color: #e74055; +// } +// .ForgotPasswordView__inputBox:focus { +// // Foundation Overrides +// box-shadow: none; +// border-color: #4a4a4a; +// } +// .ForgotPasswordView__inputError { +// font-size: 12; +// line-height: 14px; +// color: #e74055; +// margin-bottom: 13px; +// font-style: italic; +// } +// .ForgotPasswordView__link { +// font-size: 14px; +// line-height: 30px; +// margin: 4px 0 0 40px; +// } +// .ForgotPasswordView__button { +// min-width: 180px; +// .loader { +// display: none; +// } +// &.loading { +// pointer-events: none; +// .loader { +// display: inline-block; +// height: 12px; +// width: 12px; +// } +// .title { +// display: none; +// } +// } +// &:focus { +// outline: 0; + +// .loader:after { +// background: #760176; +// } +// } +// } +// .loader { +// font-size: 10px; +// text-indent: -9999em; +// width: 10rem; +// height: 10rem; +// border-radius: 50%; +// background: #ffffff; +// background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); +// background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); +// background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); +// background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); +// background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); +// position: relative; +// -webkit-animation: load3 1.4s infinite linear; +// animation: load3 1.4s infinite linear; +// -webkit-transform: translateZ(0); +// -ms-transform: translateZ(0); +// transform: translateZ(0); +// } +// .loader:before { +// width: 50%; +// height: 50%; +// background: #ffffff; +// border-radius: 100% 0 0 0; +// position: absolute; +// top: 0; +// left: 0; +// content: ''; +// } +// .loader:after { +// background: #930194; +// width: 75%; +// height: 75%; +// border-radius: 50%; +// content: ''; +// margin: auto; +// position: absolute; +// top: 0; +// left: 0; +// bottom: 0; +// right: 0; +// } + +// @-webkit-keyframes load3 { +// 0% { +// -webkit-transform: rotate(0deg); +// transform: rotate(0deg); +// } +// 100% { +// -webkit-transform: rotate(360deg); +// transform: rotate(360deg); +// } +// } +// @keyframes load3 { +// 0% { +// -webkit-transform: rotate(0deg); +// transform: rotate(0deg); +// } +// 100% { +// -webkit-transform: rotate(360deg); +// transform: rotate(360deg); +// } +// } +// @media only screen and (max-width: 740px) { +// .ForgotPasswordView__header { +// flex-direction: column; +// } +// } diff --git a/app/shared-components/index.js b/app/shared-components/index.js index 6b8e4f56e..dcc550c36 100644 --- a/app/shared-components/index.js +++ b/app/shared-components/index.js @@ -22,7 +22,7 @@ import SteppedNavigation from './SteppedNavigation'; import ToastMessage from './ToastMessage'; import ToggleCheckbox from './ToggleCheckbox'; import ToggleSwitch from './ToggleSwitch'; -import ForgotPassword from './ForgotPassword'; +import ForgotPassword from './ForgotPassword/ForgotPasswordContainer'; export { ExitButton, From b6a8814dd9397595646bdfa0f5b25ecae0c20b73 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 10:43:44 -0400 Subject: [PATCH 07/13] Use shared ForgotPassword component in hub and panel --- .../ForgotPasswordView/ForgotPasswordView.jsx | 102 ------- .../ForgotPasswordView.scss | 161 ----------- .../ForgotPasswordViewContainer.jsx | 103 ------- app/hub/Views/ForgotPasswordView/index.js | 46 --- app/hub/index.jsx | 2 +- app/panel/components/ForgotPassword.jsx | 118 -------- app/panel/index.jsx | 2 +- app/scss/hub.scss | 1 - .../ForgotPassword/ForgotPassword.jsx | 37 ++- .../ForgotPassword/ForgotPassword.scss | 270 +++++++----------- .../ForgotPassword/ForgotPasswordContainer.js | 3 +- 11 files changed, 131 insertions(+), 714 deletions(-) delete mode 100644 app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx delete mode 100644 app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss delete mode 100644 app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx delete mode 100644 app/hub/Views/ForgotPasswordView/index.js delete mode 100644 app/panel/components/ForgotPassword.jsx diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx deleted file mode 100644 index 941546ac8..000000000 --- a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.jsx +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Forgot Password View - * - * 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 from 'react'; -import PropTypes from 'prop-types'; -import { NavLink } from 'react-router-dom'; -import ClassNames from 'classnames'; -/** - * @class Implement Forgot Password view which opens from the link on log-in page in the intro hub. - * @memberof HubComponents - */ -const ForgotPasswordView = (props) => { - const { - email, - emailError, - handleInputChange, - handleSubmit, - loading - } = props; - const emailLabelClassNames = ClassNames('ForgotPasswordView__inputLabel', { - error: emailError - }); - const emailInputClassNames = ClassNames('ForgotPasswordView__inputBox', { - error: emailError - }); - const emailAsteriskClassNames = ClassNames('ForgotPasswordView__asterisk', { - error: emailError - }); - const buttonClasses = ClassNames('ForgotPasswordView__button button success', { loading }); - return ( -
-
-
-
-
-

- {t('forgot_password_message')} -

-
-
-
-
-
-
- - - - {props.emailError && ( -
- {t('invalid_email_forgot')} -
- )} -
-
-

- - {t('button_cancel')} - -

-
- -
- -
-
-
- ); -}; - -// PropTypes ensure we pass required props of the correct type -ForgotPasswordView.propTypes = { - email: PropTypes.string.isRequired, - emailError: PropTypes.bool.isRequired, - loading: PropTypes.bool.isRequired, -}; - -export default ForgotPasswordView; diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss b/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss deleted file mode 100644 index d0f21b86d..000000000 --- a/app/hub/Views/ForgotPasswordView/ForgotPasswordView.scss +++ /dev/null @@ -1,161 +0,0 @@ -/** - * ForgotPasswordView Sass - * - * 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 - */ - -.ForgotPasswordView { - margin-top: 40px; - padding-bottom: 40px; -} -.ForgotPasswordView--addPaddingTop { - padding-top: 40px; -} -.ForgotPasswordView__inputLabel { - font-size: 14px; - font-weight: 500; - line-height: 40px; - color: #4a4a4a; - &.error { - color: #e74055; - } -} -.ForgotPasswordView__asterisk { - display: none; - &.error { - display: inline; - color: #e74055; - } -} -.ForgotPasswordView__inputBox { - font-size: 14; - line-height: 24px; - color: #4a4a4a; - margin-bottom: 35px; - width: 456px; - - // Foundation Overrides - border-radius: 0; - box-shadow: none; - border: 1px solid #c8c7c2; -} -.ForgotPasswordView__inputBox.error { - margin-bottom: 8px; - border-color: #e74055; -} -.ForgotPasswordView__inputBox:focus { - // Foundation Overrides - box-shadow: none; - border-color: #4a4a4a; -} -.ForgotPasswordView__inputError { - font-size: 12; - line-height: 14px; - color: #e74055; - margin-bottom: 13px; - font-style: italic; -} -.ForgotPasswordView__link { - font-size: 14px; - line-height: 30px; - margin: 4px 0 0 40px; -} -.ForgotPasswordView__button { - min-width: 180px; - .loader { - display: none; - } - &.loading { - pointer-events: none; - .loader { - display: inline-block; - height: 12px; - width: 12px; - } - .title { - display: none; - } - } - &:focus { - outline: 0; - - .loader:after { - background: #760176; - } - } -} -.loader { - font-size: 10px; - text-indent: -9999em; - width: 10rem; - height: 10rem; - border-radius: 50%; - background: #ffffff; - background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); - position: relative; - -webkit-animation: load3 1.4s infinite linear; - animation: load3 1.4s infinite linear; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -.loader:before { - width: 50%; - height: 50%; - background: #ffffff; - border-radius: 100% 0 0 0; - position: absolute; - top: 0; - left: 0; - content: ''; -} -.loader:after { - background: #930194; - width: 75%; - height: 75%; - border-radius: 50%; - content: ''; - margin: auto; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; -} - -@-webkit-keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@media only screen and (max-width: 740px) { - .ForgotPasswordView__header { - flex-direction: column; - } -} diff --git a/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx b/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx deleted file mode 100644 index c7bb2ac47..000000000 --- a/app/hub/Views/ForgotPasswordView/ForgotPasswordViewContainer.jsx +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Forgot Password View Container - * - * 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, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { validateEmail } from '../../../../src/utils/utils'; -import ForgotPasswordView from './ForgotPasswordView'; - -/** - * @class Implement the Forgot Password View for the Ghostery Hub - * @extends Component - * @memberof HubContainers - */ -class ForgotPasswordViewContainer extends Component { - constructor(props) { - super(props); - this.state = { - email: '', - emailError: false, - loading: false, - }; - } - - /** - * Update input values by updating state. - * @param {Object} event the 'change' event - */ - handleInputChange = (event) => { - const { name, value } = event.target; - this.setState({ [name]: value }); - } - - /** - * Forgot Password Submit button - * @param {String} email the account email who's password should be reset - */ - handleSubmit = (e) => { - e.preventDefault(); - this.setState({ loading: true }, () => { - const { email } = this.state; - - // validate the email and password - if (!validateEmail(email)) { - this.setState({ - emailError: true, - loading: false, - }); - return; - } - - // Try to reset the password and display a success/error message - this.props.actions.resetPassword(email) - .then((success) => { - this.setState({ loading: false }); - if (success) { - this.props.history.push('/log-in'); - } - this.props.actions.setToast({ - toastMessage: this.props.toastMessage, - toastClass: this.props.resetPasswordError ? 'alert' : 'success', - }); - }); - }); - } - - /** - * React's required render function. Returns JSX - * @return {JSX} JSX for rendering the Create Account View of the Hub app - */ - render() { - const { email, emailError, loading } = this.state; - const childProps = { - email, - emailError, - loading, - handleInputChange: this.handleInputChange, - handleSubmit: this.handleSubmit - }; - return ( - - ); - } -} - -// PropTypes ensure we pass required props of the correct type -ForgotPasswordViewContainer.propTypes = { - actions: PropTypes.shape({ - setToast: PropTypes.func.isRequired, - resetPassword: PropTypes.func.isRequired, - }).isRequired, -}; - -export default ForgotPasswordViewContainer; diff --git a/app/hub/Views/ForgotPasswordView/index.js b/app/hub/Views/ForgotPasswordView/index.js deleted file mode 100644 index ab76a30c4..000000000 --- a/app/hub/Views/ForgotPasswordView/index.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Point of entry index.js file for Forgot Password View - * - * 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 { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; - -import ForgotPasswordViewContainer from './ForgotPasswordViewContainer'; -import AccountReducer from '../../../Account/AccountReducer'; -import { setToast } from '../AppView/AppViewActions'; -import { resetPassword } from '../../../Account/AccountActions'; - -/** - * Map redux store state properties to the component's own properties. - * @param {Object} state entire Redux store's state - * @return {function} this function returns a plain object, which will be merged into the component's props - * @memberof HubContainers - */ -const mapStateToProps = state => Object.assign({}, state.account); - -/** - * Bind the component's action creators using Redux's bindActionCreators. - * @param {function} dispatch redux store method which dispatches actions - * @return {function} to be used as an argument in redux connect call - * @memberof SetupContainers - */ - -const mapDispatchToProps = dispatch => ({ - actions: bindActionCreators(Object.assign({}, { - setToast, - resetPassword, - }), dispatch), -}); - -export const reducer = AccountReducer; - -export default connect(mapStateToProps, mapDispatchToProps)(ForgotPasswordViewContainer); diff --git a/app/hub/index.jsx b/app/hub/index.jsx index 20c7067cd..9f6590bbe 100644 --- a/app/hub/index.jsx +++ b/app/hub/index.jsx @@ -46,7 +46,7 @@ const Hub = () => ( - } /> + } /> ); diff --git a/app/panel/components/ForgotPassword.jsx b/app/panel/components/ForgotPassword.jsx deleted file mode 100644 index c8f47bb2e..000000000 --- a/app/panel/components/ForgotPassword.jsx +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Forgot Password Component - * - * 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 from 'react'; -import { Link } from 'react-router-dom'; -import ClassNames from 'classnames'; -import { validateEmail } from '../../../src/utils/utils'; -/** - * @class Implement Forgot Password view which opens from the link on Sign In panel. - * @memberof PanelClasses - */ -class ForgotPassword extends React.Component { - constructor(props) { - super(props); - this.state = { - email: '', - loading: false, - emailError: false, - }; - } - - /** - * Update state with changed values. - * @param {Object} event 'change' event - */ - handleInputChange = (e) => { - const { name, value } = e.target; - this.setState({ [name]: value }); - } - - /** - * Validate entered data, notify user if validation fails, - * This action is one the PanelActions. - */ - handleSubmit = (e) => { - e.preventDefault(); - this.setState({ loading: true }, () => { - const { email } = this.state; - - // validate the email and password - if (!validateEmail(email)) { - this.setState({ - emailError: true, - loading: false, - }); - return; - } - - this.props.actions.resetPassword(email) - .then((success) => { - this.setState({ loading: false }); - if (success) { - this.props.history.push('/login'); - } - }); - }); - } - - /** - * Render Forgot Password panel. - * @return {ReactComponent} ReactComponent instance - */ - render() { - const { email, loading, emailError } = this.state; - const buttonClasses = ClassNames('button ghostery-button', { loading }); - return ( -
-
-
-
-

- { t('forgot_password_message') } -

-
- -

- { t('invalid_email_forgot') } -

-

- { t('error_email_forgot') } -

-
-
-
- - { t('button_cancel') } - -
-
- -
-
-
-
-
-
- ); - } -} - -export default ForgotPassword; diff --git a/app/panel/index.jsx b/app/panel/index.jsx index b32c1423c..3ab83e39e 100644 --- a/app/panel/index.jsx +++ b/app/panel/index.jsx @@ -51,7 +51,7 @@ const Ghostery = () => ( - } /> + } /> ); diff --git a/app/scss/hub.scss b/app/scss/hub.scss index 4a294376a..c53e21f19 100644 --- a/app/scss/hub.scss +++ b/app/scss/hub.scss @@ -71,7 +71,6 @@ html, body, #root { @import '../hub/Views/ProductsView/ProductsView.scss'; @import '../hub/Views/SignedInView/SignedInView.scss'; @import '../hub/Views/LogInView/LogInView.scss'; -@import '../hub/Views/ForgotPasswordView/ForgotPasswordView.scss'; @import '../hub/Views/CreateAccountView/CreateAccountView.scss'; // Imports from ../shared-components directory diff --git a/app/shared-components/ForgotPassword/ForgotPassword.jsx b/app/shared-components/ForgotPassword/ForgotPassword.jsx index 8bed0bf54..ffb734699 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.jsx +++ b/app/shared-components/ForgotPassword/ForgotPassword.jsx @@ -12,11 +12,11 @@ */ import React from 'react'; -import { Link } from 'react-router-dom'; +import { withRouter, Link } from 'react-router-dom'; import ClassNames from 'classnames'; import { validateEmail } from '../../../src/utils/utils'; /** - * @class Implement Forgot Password view which opens from the link on Sign In panel. + * @class Implement shared Forgot Password view which opens from the link on Sign In page inside the panel and hub * @memberof PanelClasses */ class ForgotPassword extends React.Component { @@ -46,6 +46,7 @@ class ForgotPassword extends React.Component { e.preventDefault(); this.setState({ loading: true }, () => { const { email } = this.state; + const { locale } = this.props; // validate the email and password if (!validateEmail(email)) { @@ -59,9 +60,15 @@ class ForgotPassword extends React.Component { this.props.actions.resetPassword(email) .then((success) => { this.setState({ loading: false }); - if (success) { - // this.props.history.push('/log-in'); - // go back to hub sign in screen + if (success && locale === 'hub') { + this.props.history.push('/log-in'); + + this.props.actions.setToast({ + toastMessage: t('banner_check_your_email_title'), + toastClass: 'success', + }); + } else if (success && locale === 'panel') { + this.props.history.push('/login'); } }); }); @@ -73,25 +80,25 @@ class ForgotPassword extends React.Component { */ render() { const { email, loading, emailError } = this.state; - const { location } = this.props; + const { locale } = this.props; const buttonClasses = ClassNames('button ghostery-button', { loading }); const ContainerClassNames = ClassNames('', { - 'forgot-password-panel': location === 'panel', - ForgotPasswordView: location === 'hub', + 'forgot-password-panel': locale === 'panel', + ForgotPasswordView: locale === 'hub', }); const MessageClassNames = ClassNames('', { - 'forgot-password-message': location === 'panel', - ForgotPasswordMessage: location === 'hub', + 'forgot-password-message': locale === 'panel', + ForgotPasswordMessage: locale === 'hub', }); const EmailClassNames = ClassNames('', { - 'forgot-input-email': location === 'panel', - ForgotPasswordMessage: location === 'hub', + 'forgot-input-email': locale === 'panel', + ForgotPasswordMessage: locale === 'hub', }); const ButtonsContainerClassNames = ClassNames('row', { - 'buttons-container': location === 'panel', - ForgotPasswordButtonsContainer: location === 'hub', + 'buttons-container': locale === 'panel', + ForgotPasswordButtonsContainer: locale === 'hub', }); return (
@@ -135,4 +142,4 @@ class ForgotPassword extends React.Component { } } -export default ForgotPassword; +export default withRouter(ForgotPassword); diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss index def80744e..e286a00f7 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.scss +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -7,18 +7,7 @@ color: #333333; margin-bottom: 50px; } - #forgot-email { - &.not-found-error { - p.warning.invalid-email { - opacity: 0; - } - } - &.invalid-email { - p.warning.not-found-error { - opacity: 0; - } - } - } + .buttons-container { margin-top: 60px; #send-button { @@ -34,23 +23,24 @@ #ForgotPasswordMessage { width: 456px; } - #forgot-email { - &.not-found-error { - p.warning.invalid-email { - opacity: 0; - } - } - &.invalid-email { - p.warning.not-found-error { - opacity: 0; - } - } - } .ForgotPasswordButtonsContainer { width: 456px; } } +/* FORGOT PASSWORD SHARED */ +#forgot-email { + &.not-found-error { + p.warning.invalid-email { + opacity: 0; + } + } + &.invalid-email { + p.warning.not-found-error { + opacity: 0; + } + } +} p.warning { margin: 0; font-size: 12px; @@ -75,147 +65,97 @@ p.warning { } } -// .ForgotPasswordView--addPaddingTop { -// padding-top: 40px; -// } -// .ForgotPasswordView__inputLabel { -// font-size: 14px; -// font-weight: 500; -// line-height: 40px; -// color: #4a4a4a; -// &.error { -// color: #e74055; -// } -// } -// .ForgotPasswordView__asterisk { -// display: none; -// &.error { -// display: inline; -// color: #e74055; -// } -// } -// .ForgotPasswordView__inputBox { -// font-size: 14; -// line-height: 24px; -// color: #4a4a4a; -// margin-bottom: 35px; -// width: 456px; +/* Loading icon */ +.button.ghostery-button { + min-width: 180px; + .loader { + display: none; + } + &.loading { + pointer-events: none; + .loader { + display: inline-block; + height: 12px; + width: 12px; + } + .title { + display: none; + } + } + &:focus { + outline: 0; -// // Foundation Overrides -// border-radius: 0; -// box-shadow: none; -// border: 1px solid #c8c7c2; -// } -// .ForgotPasswordView__inputBox.error { -// margin-bottom: 8px; -// border-color: #e74055; -// } -// .ForgotPasswordView__inputBox:focus { -// // Foundation Overrides -// box-shadow: none; -// border-color: #4a4a4a; -// } -// .ForgotPasswordView__inputError { -// font-size: 12; -// line-height: 14px; -// color: #e74055; -// margin-bottom: 13px; -// font-style: italic; -// } -// .ForgotPasswordView__link { -// font-size: 14px; -// line-height: 30px; -// margin: 4px 0 0 40px; -// } -// .ForgotPasswordView__button { -// min-width: 180px; -// .loader { -// display: none; -// } -// &.loading { -// pointer-events: none; -// .loader { -// display: inline-block; -// height: 12px; -// width: 12px; -// } -// .title { -// display: none; -// } -// } -// &:focus { -// outline: 0; + .loader:after { + background: #760176; + } + } +} -// .loader:after { -// background: #760176; -// } -// } -// } -// .loader { -// font-size: 10px; -// text-indent: -9999em; -// width: 10rem; -// height: 10rem; -// border-radius: 50%; -// background: #ffffff; -// background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); -// background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); -// background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); -// background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); -// background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); -// position: relative; -// -webkit-animation: load3 1.4s infinite linear; -// animation: load3 1.4s infinite linear; -// -webkit-transform: translateZ(0); -// -ms-transform: translateZ(0); -// transform: translateZ(0); -// } -// .loader:before { -// width: 50%; -// height: 50%; -// background: #ffffff; -// border-radius: 100% 0 0 0; -// position: absolute; -// top: 0; -// left: 0; -// content: ''; -// } -// .loader:after { -// background: #930194; -// width: 75%; -// height: 75%; -// border-radius: 50%; -// content: ''; -// margin: auto; -// position: absolute; -// top: 0; -// left: 0; -// bottom: 0; -// right: 0; -// } +.loader { + font-size: 10px; + text-indent: -9999em; + width: 10px; + height: 10rem; + border-radius: 50%; + background: #ffffff; + background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%); + position: relative; + -webkit-animation: load3 1.4s infinite linear; + animation: load3 1.4s infinite linear; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); +} +.loader:before { + width: 50%; + height: 50%; + background: #ffffff; + border-radius: 100% 0 0 0; + position: absolute; + top: 0; + left: 0; + content: ''; +} +.loader:after { + background: #930194; + width: 75%; + height: 75%; + border-radius: 50%; + content: ''; + margin: auto; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; +} -// @-webkit-keyframes load3 { -// 0% { -// -webkit-transform: rotate(0deg); -// transform: rotate(0deg); -// } -// 100% { -// -webkit-transform: rotate(360deg); -// transform: rotate(360deg); -// } -// } -// @keyframes load3 { -// 0% { -// -webkit-transform: rotate(0deg); -// transform: rotate(0deg); -// } -// 100% { -// -webkit-transform: rotate(360deg); -// transform: rotate(360deg); -// } -// } -// @media only screen and (max-width: 740px) { -// .ForgotPasswordView__header { -// flex-direction: column; -// } -// } +@-webkit-keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes load3 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@media only screen and (max-width: 740px) { + .ForgotPasswordView__header { + flex-direction: column; + } +} diff --git a/app/shared-components/ForgotPassword/ForgotPasswordContainer.js b/app/shared-components/ForgotPassword/ForgotPasswordContainer.js index 098f93bbe..0add4b2dc 100644 --- a/app/shared-components/ForgotPassword/ForgotPasswordContainer.js +++ b/app/shared-components/ForgotPassword/ForgotPasswordContainer.js @@ -15,6 +15,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import ForgotPassword from './ForgotPassword'; import * as actions from '../../panel/actions/PanelActions'; // get shared actions from Panel +import { setToast } from '../../hub/Views/AppView/AppViewActions'; import { resetPassword } from '../../Account/AccountActions'; /** * Map redux store state properties to ForgotPassword component own properties. @@ -33,7 +34,7 @@ const mapStateToProps = () => Object.assign({}); * @param {Object} ownProps ForgotPassword component own props * @return {function} to be used as an argument in redux connect call */ -const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign(actions, { resetPassword }), dispatch) }); +const mapDispatchToProps = dispatch => ({ actions: bindActionCreators(Object.assign(actions, { resetPassword, setToast }), dispatch) }); /** * Connect ForgotPassword component to the Redux store. * @memberOf PanelContainers From ddd9dddd055848b7971a3a0876468c6dd5344284 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 10:51:24 -0400 Subject: [PATCH 08/13] Move functions back to app/panel/utils --- .../CreateAccountViewContainer.jsx | 2 +- app/panel/utils/utils.js | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx b/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx index cee68b7e4..f0099d3b2 100644 --- a/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx +++ b/app/hub/Views/CreateAccountView/CreateAccountViewContainer.jsx @@ -18,7 +18,7 @@ import { validatePassword, validateEmailsMatch, validateConfirmEmail -} from '../../../../src/utils/utils'; +} from '../../../panel/utils/utils'; import CreateAccountView from './CreateAccountView'; import SignedInView from '../SignedInView'; diff --git a/app/panel/utils/utils.js b/app/panel/utils/utils.js index 1d519bcb1..a92dde970 100644 --- a/app/panel/utils/utils.js +++ b/app/panel/utils/utils.js @@ -110,6 +110,61 @@ export function computeTimeDelta(start, end) { }; } +/** + * Check for valid email + * @memberOf PanelUtils + * @param {string} email email to validate + * @return {boolean} true if valid, false otherwise + */ +export function validateEmail(email) { + // eslint-disable-next-line no-useless-escape, no-control-regex + const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i; + return email !== '' && emailRegex.test(email); +} + +/** + * Check for valid confirm email and equality to email + * @memberOf PanelUtils + * @param {string} email email to validate + * @param {string} confirmEmail confirm email to validate + * @return {boolean} true if valid, false otherwise + */ +export function validateConfirmEmail(email, confirmEmail) { + if (!email || !confirmEmail) { + return false; + } + const lEmail = email.toLowerCase(); + const lConfirmEmail = confirmEmail.toLowerCase(); + return validateEmail(confirmEmail) && (lEmail === lConfirmEmail) || false; +} + +/** + * Check for confirm email equality to email + * @memberOf PanelUtils + * @param {string} email email + * @param {string} confirmEmail confirm email to validate + * @return {boolean} true if equal, false otherwise + */ +export function validateEmailsMatch(email, confirmEmail) { + if (!email || !confirmEmail) { + return false; + } + const lEmail = email.toLowerCase(); + const lConfirmEmail = confirmEmail.toLowerCase(); + return lEmail === lConfirmEmail; +} + +/** + * Check for valid password + * @memberOf PanelUtils + * @param {string} pwd password to validate + * @return {boolean} true if valid, false otherwise + */ +export function validatePassword(pwd) { + const pwdRegex = /^[a-zA-Z0-9!@#$%^&*=+()<>{}[\];:,./?]{8,50}$/; + return pwd !== '' && pwdRegex.test(pwd); +} + /** * Helper method for making XHR requests * @memberOf PanelUtils From 1e249b12842f1fc098d4496d6b9505e1864fcf13 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 11:14:24 -0400 Subject: [PATCH 09/13] Delete functions from app/panel/utils. Remove console log --- app/Account/AccountActions.js | 1 - src/utils/utils.js | 55 ----------------------------------- 2 files changed, 56 deletions(-) diff --git a/app/Account/AccountActions.js b/app/Account/AccountActions.js index 77563155f..a3851dd40 100644 --- a/app/Account/AccountActions.js +++ b/app/Account/AccountActions.js @@ -166,7 +166,6 @@ export const logout = () => dispatch => ( ); export const resetPassword = email => dispatch => ( - // console.log('resetting password...'); sendMessageInPromise('account.resetPassword', { email }) .then((res) => { const { errors } = res; diff --git a/src/utils/utils.js b/src/utils/utils.js index 37f75cf63..e4f261523 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -617,58 +617,3 @@ export function injectNotifications(tab_id, importExport = false) { export function isCliqzOffer(offer) { return (offer && offer.origin === 'cliqz' && offer.type === 'offers' && offer.data); } - -/** - * Check for valid email - * @memberOf PanelUtils - * @param {string} email email to validate - * @return {boolean} true if valid, false otherwise - */ -export function validateEmail(email) { - // eslint-disable-next-line no-useless-escape, no-control-regex - const emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i; - return email !== '' && emailRegex.test(email); -} - -/** - * Check for valid confirm email and equality to email - * @memberOf PanelUtils - * @param {string} email email to validate - * @param {string} confirmEmail confirm email to validate - * @return {boolean} true if valid, false otherwise - */ -export function validateConfirmEmail(email, confirmEmail) { - if (!email || !confirmEmail) { - return false; - } - const lEmail = email.toLowerCase(); - const lConfirmEmail = confirmEmail.toLowerCase(); - return validateEmail(confirmEmail) && (lEmail === lConfirmEmail) || false; -} - -/** - * Check for confirm email equality to email - * @memberOf PanelUtils - * @param {string} email email - * @param {string} confirmEmail confirm email to validate - * @return {boolean} true if equal, false otherwise - */ -export function validateEmailsMatch(email, confirmEmail) { - if (!email || !confirmEmail) { - return false; - } - const lEmail = email.toLowerCase(); - const lConfirmEmail = confirmEmail.toLowerCase(); - return lEmail === lConfirmEmail; -} - -/** - * Check for valid password - * @memberOf PanelUtils - * @param {string} pwd password to validate - * @return {boolean} true if valid, false otherwise - */ -export function validatePassword(pwd) { - const pwdRegex = /^[a-zA-Z0-9!@#$%^&*=+()<>{}[\];:,./?]{8,50}$/; - return pwd !== '' && pwdRegex.test(pwd); -} From 083fa317dcbbe48f9c812701b6a50aa89006512d Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 11:25:00 -0400 Subject: [PATCH 10/13] Fix imports for shared utils functions --- app/hub/Views/LogInView/LogInViewContainer.jsx | 2 +- app/panel/components/CreateAccount.jsx | 2 +- app/panel/components/Login.jsx | 2 +- app/shared-components/ForgotPassword/ForgotPassword.jsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/hub/Views/LogInView/LogInViewContainer.jsx b/app/hub/Views/LogInView/LogInViewContainer.jsx index 48e58fcbf..fb9cd1289 100644 --- a/app/hub/Views/LogInView/LogInViewContainer.jsx +++ b/app/hub/Views/LogInView/LogInViewContainer.jsx @@ -13,7 +13,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { validateEmail } from '../../../../src/utils/utils'; +import { validateEmail } from '../../../panel/utils/utils'; import LogInView from './LogInView'; import SignedInView from '../SignedInView'; /** diff --git a/app/panel/components/CreateAccount.jsx b/app/panel/components/CreateAccount.jsx index 93bc85c38..7d0120504 100644 --- a/app/panel/components/CreateAccount.jsx +++ b/app/panel/components/CreateAccount.jsx @@ -15,7 +15,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import ClassNames from 'classnames'; import RSVP from 'rsvp'; -import { validateEmail, validateConfirmEmail, validatePassword } from '../../../src/utils/utils'; +import { validateEmail, validateConfirmEmail, validatePassword } from '../utils/utils'; import I18nWithLink from '../../shared-components/I18nWithLink'; /** diff --git a/app/panel/components/Login.jsx b/app/panel/components/Login.jsx index 195cc36d0..a7b7562d4 100644 --- a/app/panel/components/Login.jsx +++ b/app/panel/components/Login.jsx @@ -15,7 +15,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import ClassNames from 'classnames'; import RSVP from 'rsvp'; -import { validateEmail } from '../../../src/utils/utils'; +import { validateEmail } from '../utils/utils'; import { log } from '../../../src/utils/common'; import history from '../utils/history'; diff --git a/app/shared-components/ForgotPassword/ForgotPassword.jsx b/app/shared-components/ForgotPassword/ForgotPassword.jsx index ffb734699..1c4fe6621 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.jsx +++ b/app/shared-components/ForgotPassword/ForgotPassword.jsx @@ -14,7 +14,7 @@ import React from 'react'; import { withRouter, Link } from 'react-router-dom'; import ClassNames from 'classnames'; -import { validateEmail } from '../../../src/utils/utils'; +import { validateEmail } from '../../panel/utils/utils'; /** * @class Implement shared Forgot Password view which opens from the link on Sign In page inside the panel and hub * @memberof PanelClasses From 53681db7b0b959f6ed08b88ea4c9d80ac229adb8 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 11:32:43 -0400 Subject: [PATCH 11/13] Minor css changes --- app/shared-components/ForgotPassword/ForgotPassword.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss index e286a00f7..d40da93e5 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.scss +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -21,9 +21,11 @@ margin: 40px auto; padding-bottom: 40px; #ForgotPasswordMessage { + margin-top: 40px; width: 456px; } .ForgotPasswordButtonsContainer { + margin-top: 10px; width: 456px; } } @@ -67,7 +69,6 @@ p.warning { /* Loading icon */ .button.ghostery-button { - min-width: 180px; .loader { display: none; } From bb862bd380958f0ab9f4bbaaf99eb4890cdd8ff4 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 14:09:09 -0400 Subject: [PATCH 12/13] More minor css changes --- app/shared-components/ForgotPassword/ForgotPassword.scss | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss index d40da93e5..00fad4222 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.scss +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -20,8 +20,10 @@ #ForgotPasswordView { margin: 40px auto; padding-bottom: 40px; - #ForgotPasswordMessage { + #forgot-email { margin-top: 40px; + } + #ForgotPasswordMessage { width: 456px; } .ForgotPasswordButtonsContainer { @@ -53,6 +55,11 @@ p.warning { opacity: 0; @include transition(opacity 0.2s, color 0.2s); } +span.asterisk { + display: none; + font-size: 14px; + margin: 0; +} .panel-error { span.asterisk {display: inline;} p.warning { From 2beef10f7875fe9c5516297946d7783e0857030a Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 16 Apr 2020 16:48:15 -0400 Subject: [PATCH 13/13] Add document header to sass file --- .../ForgotPassword/ForgotPassword.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/shared-components/ForgotPassword/ForgotPassword.scss b/app/shared-components/ForgotPassword/ForgotPassword.scss index 00fad4222..78ea1ef31 100644 --- a/app/shared-components/ForgotPassword/ForgotPassword.scss +++ b/app/shared-components/ForgotPassword/ForgotPassword.scss @@ -1,3 +1,16 @@ +/** + * Forgot Password Sass + * + * 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 + */ + /* FORGOT PASSWORD PANEL */ #forgot-password-panel{ margin-top: 75px;