diff --git a/src/forgot-password/ForgotPasswordPage.jsx b/src/forgot-password/ForgotPasswordPage.jsx index 72d39bbc..591c75fb 100644 --- a/src/forgot-password/ForgotPasswordPage.jsx +++ b/src/forgot-password/ForgotPasswordPage.jsx @@ -19,7 +19,7 @@ import { } from '@edx/paragon'; import { ChevronLeft } from '@edx/paragon/icons'; -import { forgotPassword, setForgotPasswordFormData } from './data/actions'; +import { forgotPassword } from './data/actions'; import { forgotPasswordResultSelector } from './data/selectors'; import messages from './messages'; @@ -34,7 +34,7 @@ const ForgotPasswordPage = (props) => { const platformName = getConfig().SITE_NAME; const regex = new RegExp(VALID_EMAIL_REGEX, 'i'); - const [validationError, setValidationError] = useState(props.emailValidationError || ''); + const [validationError, setValidationError] = useState(''); const [key, setKey] = useState(''); const supportUrl = getConfig().LOGIN_ISSUE_SUPPORT_LINK; @@ -56,11 +56,6 @@ const ForgotPasswordPage = (props) => { return error; }; - const onBlur = (email) => { - const emailValidationError = getValidationMessage(email); - props.setForgotPasswordFormData({ email, emailValidationError }); - }; - const tabTitle = (
@@ -79,7 +74,7 @@ const ForgotPasswordPage = (props) => { )}
{ const validationMessage = getValidationMessage(values.email); @@ -115,7 +110,7 @@ const ForgotPasswordPage = (props) => { name="email" errorMessage={validationError} value={values.email} - handleBlur={() => onBlur(values.email)} + handleBlur={() => getValidationMessage(values.email)} handleChange={e => setFieldValue('email', e.target.value)} handleFocus={() => setValidationError('')} helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]} @@ -161,16 +156,13 @@ const ForgotPasswordPage = (props) => { ForgotPasswordPage.propTypes = { intl: intlShape.isRequired, email: PropTypes.string, - emailValidationError: PropTypes.string, forgotPassword: PropTypes.func.isRequired, - setForgotPasswordFormData: PropTypes.func.isRequired, status: PropTypes.string, submitState: PropTypes.string, }; ForgotPasswordPage.defaultProps = { email: '', - emailValidationError: '', status: null, submitState: DEFAULT_STATE, }; @@ -179,6 +171,5 @@ export default connect( forgotPasswordResultSelector, { forgotPassword, - setForgotPasswordFormData, }, )(injectIntl(ForgotPasswordPage)); diff --git a/src/forgot-password/data/actions.js b/src/forgot-password/data/actions.js index afbad054..dcdd871c 100644 --- a/src/forgot-password/data/actions.js +++ b/src/forgot-password/data/actions.js @@ -1,7 +1,6 @@ import { AsyncActionType } from '../../data/utils'; export const FORGOT_PASSWORD = new AsyncActionType('FORGOT', 'PASSWORD'); -export const FORGOT_PASSWORD_PERSIST_FORM_DATA = 'FORGOT_PASSWORD_PERSIST_FORM_DATA'; // Forgot Password export const forgotPassword = email => ({ @@ -25,8 +24,3 @@ export const forgotPasswordForbidden = () => ({ export const forgotPasswordServerError = () => ({ type: FORGOT_PASSWORD.FAILURE, }); - -export const setForgotPasswordFormData = (forgotPasswordFormData) => ({ - type: FORGOT_PASSWORD_PERSIST_FORM_DATA, - payload: { forgotPasswordFormData }, -}); diff --git a/src/forgot-password/data/reducers.js b/src/forgot-password/data/reducers.js index 5a729fdc..62d000fb 100644 --- a/src/forgot-password/data/reducers.js +++ b/src/forgot-password/data/reducers.js @@ -1,4 +1,4 @@ -import { FORGOT_PASSWORD, FORGOT_PASSWORD_PERSIST_FORM_DATA } from './actions'; +import { FORGOT_PASSWORD } from './actions'; import { INTERNAL_SERVER_ERROR, PENDING_STATE } from '../../data/constants'; import { PASSWORD_RESET_FAILURE } from '../../reset-password/data/actions'; @@ -6,7 +6,6 @@ export const defaultState = { status: '', submitState: '', email: '', - emailValidationError: '', }; const reducer = (state = defaultState, action = null) => { @@ -34,20 +33,8 @@ const reducer = (state = defaultState, action = null) => { return { status: action.payload.errorCode, }; - case FORGOT_PASSWORD_PERSIST_FORM_DATA: { - const { forgotPasswordFormData } = action.payload; - return { - ...state, - email: forgotPasswordFormData.email, - emailValidationError: forgotPasswordFormData.emailValidationError, - }; - } default: - return { - ...defaultState, - email: state.email, - emailValidationError: state.emailValidationError, - }; + return defaultState; } } return state; diff --git a/src/forgot-password/data/tests/reducers.test.js b/src/forgot-password/data/tests/reducers.test.js deleted file mode 100644 index e993686e..00000000 --- a/src/forgot-password/data/tests/reducers.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import reducer from '../reducers'; -import { - FORGOT_PASSWORD_PERSIST_FORM_DATA, -} from '../actions'; - -describe('forgot password reducer', () => { - it('should set email and emailValidationError', () => { - const state = { - status: '', - submitState: '', - email: '', - emailValidationError: '', - }; - const forgotPasswordFormData = { - email: 'test@gmail', - emailValidationError: 'Enter a valid email address', - }; - const action = { - type: FORGOT_PASSWORD_PERSIST_FORM_DATA, - payload: { forgotPasswordFormData }, - }; - - expect( - reducer(state, action), - ).toEqual( - { - status: '', - submitState: '', - email: 'test@gmail', - emailValidationError: 'Enter a valid email address', - }, - ); - }); -}); diff --git a/src/forgot-password/data/tests/sagas.test.js b/src/forgot-password/data/tests/sagas.test.js index dae526a4..d3e85654 100644 --- a/src/forgot-password/data/tests/sagas.test.js +++ b/src/forgot-password/data/tests/sagas.test.js @@ -10,7 +10,7 @@ const { loggingService } = initializeMockLogging(); describe('handleForgotPassword', () => { const params = { payload: { - forgotPasswordFormData: { + formData: { email: 'test@test.com', }, }, diff --git a/src/forgot-password/tests/ForgotPasswordPage.test.jsx b/src/forgot-password/tests/ForgotPasswordPage.test.jsx index 5bf39fe0..06a66618 100644 --- a/src/forgot-password/tests/ForgotPasswordPage.test.jsx +++ b/src/forgot-password/tests/ForgotPasswordPage.test.jsx @@ -16,7 +16,6 @@ import * as auth from '@edx/frontend-platform/auth'; import ForgotPasswordPage from '../ForgotPasswordPage'; import { INTERNAL_SERVER_ERROR, LOGIN_PAGE } from '../../data/constants'; import { PASSWORD_RESET } from '../../reset-password/data/constants'; -import { setForgotPasswordFormData } from '../data/actions'; jest.mock('@edx/frontend-platform/analytics'); jest.mock('@edx/frontend-platform/auth'); @@ -198,24 +197,4 @@ describe('ForgotPasswordPage', () => { forgotPasswordPage.update(); expect(history.location.pathname).toEqual(LOGIN_PAGE); }); - - // *********** persists form data on going back to sign in page *********** - - it('should set form data in redux store on onBlur', () => { - const forgotPasswordFormData = { - email: 'test@gmail', - emailValidationError: 'Enter a valid email address', - }; - - props = { - ...props, - email: 'test@gmail', - emailValidationError: '', - }; - - store.dispatch = jest.fn(store.dispatch); - const wrapper = mount(reduxWrapper()); - wrapper.find('input#email').simulate('blur'); - expect(store.dispatch).toHaveBeenCalledWith(setForgotPasswordFormData(forgotPasswordFormData)); - }); }); diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx index 8dbaeb59..90c88e67 100644 --- a/src/login/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -15,11 +15,9 @@ import { import { Institution } from '@edx/paragon/icons'; import AccountActivationMessage from './AccountActivationMessage'; -import { - loginRequest, loginRequestFailure, loginRequestReset, setLoginFormData, -} from './data/actions'; +import { loginRequest, loginRequestFailure, loginRequestReset } from './data/actions'; import { INVALID_FORM } from './data/constants'; -import { loginErrorSelector, loginFormDataSelector, loginRequestSelector } from './data/selectors'; +import { loginErrorSelector, loginRequestSelector } from './data/selectors'; import LoginFailureMessage from './LoginFailure'; import messages from './messages'; @@ -42,18 +40,20 @@ import { getAllPossibleQueryParam, updatePathWithQueryParams, } from '../data/utils'; +import { forgotPasswordResultSelector } from '../forgot-password'; import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess'; class LoginPage extends React.Component { constructor(props, context) { super(props, context); + sendPageEvent('login_and_registration', 'login'); this.state = { - password: this.props.loginFormData.password || '', - emailOrUsername: this.props.loginFormData.emailOrUsername || '', + password: '', + emailOrUsername: '', errors: { - emailOrUsername: this.props.loginFormData.errors.emailOrUsername || '', - password: this.props.loginFormData.errors.password || '', + emailOrUsername: '', + password: '', }, isSubmitted: false, }; @@ -78,18 +78,12 @@ class LoginPage extends React.Component { handleSubmit = (e) => { e.preventDefault(); this.setState({ isSubmitted: true }); + const { emailOrUsername, password } = this.state; const emailValidationError = this.validateEmail(emailOrUsername); const passwordValidationError = this.validatePassword(password); if (emailValidationError !== '' || passwordValidationError !== '') { - this.props.setLoginFormData({ - ...this.props.loginFormData, - errors: { - emailOrUsername: emailValidationError, - password: passwordValidationError, - }, - }); this.props.loginRequestFailure({ errorCode: INVALID_FORM, }); @@ -105,18 +99,7 @@ class LoginPage extends React.Component { handleOnFocus = (e) => { const { errors } = this.state; errors[e.target.name] = ''; - this.props.setLoginFormData({ - ...this.props.loginFormData, - errors, - }); - } - - handleOnBlur = (e) => { - const payload = { - ...this.props.loginFormData, - [e.target.name]: e.target.value, - }; - this.props.setLoginFormData(payload); + this.setState({ errors }); } handleForgotPasswordLinkClickEvent = () => { @@ -133,6 +116,7 @@ class LoginPage extends React.Component { } else { errors.emailOrUsername = ''; } + this.setState({ errors }); return errors.emailOrUsername; } @@ -140,6 +124,7 @@ class LoginPage extends React.Component { const { errors } = this.state; errors.password = password.length > 0 ? '' : this.props.intl.formatMessage(messages['password.validation.message']); + this.setState({ errors }); return errors.password; } @@ -245,7 +230,6 @@ class LoginPage extends React.Component { value={this.state.emailOrUsername} handleChange={(e) => this.setState({ emailOrUsername: e.target.value, isSubmitted: false })} handleFocus={this.handleOnFocus} - handleBlur={this.handleOnBlur} errorMessage={this.state.errors.emailOrUsername} floatingLabel={intl.formatMessage(messages['login.user.identity.label'])} /> @@ -255,7 +239,6 @@ class LoginPage extends React.Component { showRequirements={false} handleChange={(e) => this.setState({ password: e.target.value, isSubmitted: false })} handleFocus={this.handleOnFocus} - handleBlur={this.handleOnBlur} errorMessage={this.state.errors.password} floatingLabel={intl.formatMessage(messages['login.password.label'])} /> @@ -327,16 +310,9 @@ class LoginPage extends React.Component { } LoginPage.defaultProps = { + forgotPassword: null, loginResult: null, loginError: null, - loginFormData: { - emailOrUsername: '', - password: '', - errors: { - emailOrUsername: '', - password: '', - }, - }, resetPassword: false, submitState: DEFAULT_STATE, thirdPartyAuthApiStatus: 'pending', @@ -349,25 +325,20 @@ LoginPage.defaultProps = { }; LoginPage.propTypes = { + forgotPassword: PropTypes.shape({ + email: PropTypes.string, + status: PropTypes.string, + }), getThirdPartyAuthContext: PropTypes.func.isRequired, intl: intlShape.isRequired, loginError: PropTypes.objectOf(PropTypes.any), loginRequest: PropTypes.func.isRequired, loginRequestFailure: PropTypes.func.isRequired, loginRequestReset: PropTypes.func.isRequired, - setLoginFormData: PropTypes.func.isRequired, loginResult: PropTypes.shape({ redirectUrl: PropTypes.string, success: PropTypes.bool, }), - loginFormData: PropTypes.shape({ - emailOrUsername: PropTypes.string, - password: PropTypes.string, - errors: PropTypes.shape({ - emailOrUsername: PropTypes.string, - password: PropTypes.string, - }), - }), resetPassword: PropTypes.bool, submitState: PropTypes.string, thirdPartyAuthApiStatus: PropTypes.string, @@ -383,17 +354,17 @@ LoginPage.propTypes = { }; const mapStateToProps = state => { + const forgotPassword = forgotPasswordResultSelector(state); const loginResult = loginRequestSelector(state); const thirdPartyAuthContext = thirdPartyAuthContextSelector(state); const loginError = loginErrorSelector(state); - const loginFormData = loginFormDataSelector(state); return { submitState: state.login.submitState, thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus, + forgotPassword, loginError, loginResult, thirdPartyAuthContext, - loginFormData, resetPassword: state.login.resetPassword, }; }; @@ -405,6 +376,5 @@ export default connect( loginRequest, loginRequestFailure, loginRequestReset, - setLoginFormData, }, )(injectIntl(LoginPage)); diff --git a/src/login/data/actions.js b/src/login/data/actions.js index 7d91b17a..198a88af 100644 --- a/src/login/data/actions.js +++ b/src/login/data/actions.js @@ -1,7 +1,6 @@ import { AsyncActionType } from '../../data/utils'; export const LOGIN_REQUEST = new AsyncActionType('LOGIN', 'REQUEST'); -export const LOGIN_PERSIST_FORM_DATA = 'LOGIN_PERSIST_FORM_DATA'; // Login export const loginRequest = creds => ({ @@ -26,8 +25,3 @@ export const loginRequestFailure = (loginError) => ({ export const loginRequestReset = () => ({ type: LOGIN_REQUEST.RESET, }); - -export const setLoginFormData = (loginFormData) => ({ - type: LOGIN_PERSIST_FORM_DATA, - payload: { loginFormData }, -}); diff --git a/src/login/data/reducers.js b/src/login/data/reducers.js index 484389d1..3937f95a 100644 --- a/src/login/data/reducers.js +++ b/src/login/data/reducers.js @@ -1,4 +1,4 @@ -import { LOGIN_REQUEST, LOGIN_PERSIST_FORM_DATA } from './actions'; +import { LOGIN_REQUEST } from './actions'; import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; import { RESET_PASSWORD } from '../../reset-password'; @@ -7,14 +7,6 @@ export const defaultState = { loginError: null, loginResult: {}, resetPassword: false, - loginFormData: { - password: '', - emailOrUsername: '', - errors: { - emailOrUsername: '', - password: '', - }, - }, }; const reducer = (state = defaultState, action) => { @@ -46,13 +38,6 @@ const reducer = (state = defaultState, action) => { ...state, resetPassword: true, }; - case LOGIN_PERSIST_FORM_DATA: { - const { loginFormData } = action.payload; - return { - ...state, - loginFormData, - }; - } default: return { ...state, diff --git a/src/login/data/selectors.js b/src/login/data/selectors.js index e0606808..a409a5ee 100644 --- a/src/login/data/selectors.js +++ b/src/login/data/selectors.js @@ -13,8 +13,3 @@ export const loginErrorSelector = createSelector( loginSelector, login => login.loginError, ); - -export const loginFormDataSelector = createSelector( - loginSelector, - login => login.loginFormData, -); diff --git a/src/login/data/tests/reducers.test.js b/src/login/data/tests/reducers.test.js deleted file mode 100644 index ef6e6c19..00000000 --- a/src/login/data/tests/reducers.test.js +++ /dev/null @@ -1,46 +0,0 @@ -import reducer from '../reducers'; -import { - LOGIN_PERSIST_FORM_DATA, -} from '../actions'; - -describe('login reducer', () => { - it('should set registrationFormData', () => { - const state = { - loginFormData: { - password: '', - emailOrUsername: '', - errors: { - emailOrUsername: '', - password: '', - }, - }, - }; - const loginFormData = { - password: 'johndoe', - emailOrUsername: 'john@gmail.com', - errors: { - emailOrUsername: '', - password: '', - }, - }; - const action = { - type: LOGIN_PERSIST_FORM_DATA, - payload: { loginFormData }, - }; - - expect( - reducer(state, action), - ).toEqual( - { - loginFormData: { - password: 'johndoe', - emailOrUsername: 'john@gmail.com', - errors: { - emailOrUsername: '', - password: '', - }, - }, - }, - ); - }); -}); diff --git a/src/login/data/tests/sagas.test.js b/src/login/data/tests/sagas.test.js index d8ccb0f8..377caf27 100644 --- a/src/login/data/tests/sagas.test.js +++ b/src/login/data/tests/sagas.test.js @@ -13,7 +13,7 @@ const { loggingService } = initializeMockLogging(); describe('handleLoginRequest', () => { const params = { payload: { - loginFormData: { + formData: { email: 'test@test.com', password: 'test-password', }, diff --git a/src/login/tests/LoginPage.test.jsx b/src/login/tests/LoginPage.test.jsx index 4e18510c..e7cea8c0 100644 --- a/src/login/tests/LoginPage.test.jsx +++ b/src/login/tests/LoginPage.test.jsx @@ -12,9 +12,7 @@ import * as analytics from '@edx/frontend-platform/analytics'; import * as auth from '@edx/frontend-platform/auth'; import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; -import { - loginRequest, loginRequestFailure, loginRequestReset, setLoginFormData, -} from '../data/actions'; +import { loginRequest, loginRequestFailure, loginRequestReset } from '../data/actions'; import LoginFailureMessage from '../LoginFailure'; import LoginPage from '../LoginPage'; @@ -37,7 +35,6 @@ describe('LoginPage', () => { }); let props = {}; let store = {}; - let loginFormData = {}; const reduxWrapper = children => ( @@ -85,14 +82,6 @@ describe('LoginPage', () => { handleInstitutionLogin: jest.fn(), institutionLogin: false, }; - loginFormData = { - emailOrUsername: '', - password: '', - errors: { - emailOrUsername: '', - password: '', - }, - }; }); // ******** test login form submission ******** @@ -476,42 +465,4 @@ describe('LoginPage', () => { expect(store.dispatch).toHaveBeenCalledWith(loginRequestReset()); expect(loginPage.state('errors')).toEqual(errorState); }); - - // persists form data tests - - it('should set errors in redux store on submit form for invalid input', () => { - loginFormData = { - ...loginFormData, - errors: { - emailOrUsername: 'Enter your username or email', - password: 'Enter your password', - }, - }; - store.dispatch = jest.fn(store.dispatch); - const loginPage = mount(reduxWrapper()); - - loginPage.find('input#emailOrUsername').simulate('change', { target: { value: '' } }); - loginPage.find('input#password').simulate('change', { target: { value: '' } }); - loginPage.find('button.btn-brand').simulate('click'); - - expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(loginFormData)); - }); - - it('should set form data in redux store on onBlur', () => { - store.dispatch = jest.fn(store.dispatch); - - const loginPage = mount(reduxWrapper()); - loginPage.find('input#emailOrUsername').simulate('blur'); - - expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(loginFormData)); - }); - - it('should clear form field errors in redux store on onFocus', () => { - store.dispatch = jest.fn(store.dispatch); - - const loginPage = mount(reduxWrapper()); - loginPage.find('input#emailOrUsername').simulate('focus'); - - expect(store.dispatch).toHaveBeenCalledWith(setLoginFormData(loginFormData)); - }); }); diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index a0014573..ee5fd44c 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -16,7 +16,7 @@ import { import { Error, Close } from '@edx/paragon/icons'; import FormFieldRenderer from '../field-renderer'; import { - clearUsernameSuggestions, registerNewUser, resetRegistrationForm, fetchRealtimeValidations, setRegistrationFormData, + clearUsernameSuggestions, registerNewUser, resetRegistrationForm, fetchRealtimeValidations, } from './data/actions'; import { FIELDS, FORM_SUBMISSION_ERROR, DEFAULT_SERVICE_PROVIDER_DOMAINS, DEFAULT_TOP_LEVEL_DOMAINS, COMMON_EMAIL_PROVIDERS, @@ -26,7 +26,6 @@ import { registrationRequestSelector, validationsSelector, usernameSuggestionsSelector, - registrationFormDataSelector, } from './data/selectors'; import messages from './messages'; import RegistrationFailure from './RegistrationFailure'; @@ -67,21 +66,20 @@ class RegistrationPage extends React.Component { this.tpaHint = getTpaHint(); this.state = { country: '', - email: this.props.registrationFormData.email || '', - name: this.props.registrationFormData.name || '', - password: this.props.registrationFormData.password || '', - username: this.props.registrationFormData.username || '', - marketingOptIn: this.props.registrationFormData.marketingOptIn || true, + email: '', + name: '', + password: '', + username: '', + marketingOptIn: true, errors: { - email: this.props.registrationFormData.errors.email || '', - name: this.props.registrationFormData.errors.name || '', - username: this.props.registrationFormData.errors.username || '', - password: this.props.registrationFormData.errors.password || '', + email: '', + name: '', + username: '', + password: '', country: '', }, - emailFieldBorderClass: this.props.registrationFormData.emailFieldBorderClass || '', - emailErrorSuggestion: this.props.registrationFormData.emailErrorSuggestion || null, - emailWarningSuggestion: this.props.registrationFormData.emailWarningSuggestion || null, + emailErrorSuggestion: null, + emailWarningSuggestion: null, errorCode: null, failureCount: 0, startTime: Date.now(), @@ -121,12 +119,6 @@ class RegistrationPage extends React.Component { }); return false; } - if (this.props.registrationFormData !== nextProps.registrationFormData) { - const state = { ...this.state, ...nextProps.registrationFormData }; - this.setState({ - ...state, - }); - } if (this.props.validationDecisions !== nextProps.validationDecisions) { const state = { errors: { ...this.state.errors, ...nextProps.validationDecisions } }; let validatePassword = false; @@ -295,27 +287,24 @@ class RegistrationPage extends React.Component { handleOnFocus = (e) => { const { errors } = this.state; errors[e.target.name] = ''; + const state = { errors }; if (e.target.name === 'username') { this.props.clearUsernameSuggestions(); } if (e.target.name === 'country') { - this.setState({ readOnly: false }); + state.readOnly = false; } if (e.target.name === 'passwordValidation') { - errors.password = ''; + state.errors.password = ''; } - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, - errors, - }); + this.setState({ ...state }); } handleSuggestionClick = (e, suggestion) => { const { errors } = this.state; if (e.target.name === 'username') { errors.username = ''; - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, + this.setState({ username: suggestion, errors, }); @@ -323,20 +312,18 @@ class RegistrationPage extends React.Component { } else if (e.target.name === 'email') { e.preventDefault(); errors.email = ''; - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, + this.setState({ + borderClass: '', email: suggestion, emailErrorSuggestion: null, emailWarningSuggestion: null, - emailFieldBorderClass: '', errors, }); } } handleUsernameSuggestionClose = () => { - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, + this.setState({ username: '', }); this.props.clearUsernameSuggestions(); @@ -365,15 +352,11 @@ class RegistrationPage extends React.Component { } }); - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, - errors, - }); + this.setState({ errors }); return isValid; } validateInput(fieldName, value, payload) { - let state = {}; const { errors } = this.state; const { intl, statusCode } = this.props; const emailRegex = new RegExp(VALID_EMAIL_REGEX, 'i'); @@ -426,11 +409,11 @@ class RegistrationPage extends React.Component { errors.email = ''; } } - state = { + this.setState({ emailWarningSuggestion, emailErrorSuggestion, - emailFieldBorderClass: emailWarningSuggestion ? 'yellow-border' : null, - }; + borderClass: emailWarningSuggestion ? 'yellow-border' : null, + }); } break; case 'name': @@ -484,21 +467,12 @@ class RegistrationPage extends React.Component { break; } - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, - ...state, - [fieldName]: value, - errors, - }); - + this.setState({ errors }); return errors; } handleOnClose() { - this.props.setRegistrationFormData({ - ...this.props.registrationFormData, - emailErrorSuggestion: null, - }); + this.setState({ emailErrorSuggestion: null }); } renderEmailFeedback() { @@ -721,7 +695,7 @@ class RegistrationPage extends React.Component { handleFocus={this.handleOnFocus} helpText={[intl.formatMessage(messages['help.text.email'])]} floatingLabel={intl.formatMessage(messages['registration.email.label'])} - borderClass={this.state.emailFieldBorderClass} + borderClass={this.state.borderClass} > {this.renderEmailFeedback()} @@ -871,24 +845,6 @@ RegistrationPage.defaultProps = { secondaryProviders: [], pipelineUserDetails: null, }, - registrationFormData: { - country: '', - email: '', - name: '', - password: '', - username: '', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailFieldBorderClass: '', - emailErrorSuggestion: null, - emailWarningSuggestion: null, - }, validationDecisions: null, statusCode: null, usernameSuggestions: [], @@ -901,7 +857,6 @@ RegistrationPage.propTypes = { getThirdPartyAuthContext: PropTypes.func.isRequired, registerNewUser: PropTypes.func, resetRegistrationForm: PropTypes.func.isRequired, - setRegistrationFormData: PropTypes.func.isRequired, registrationResult: PropTypes.shape({ redirectUrl: PropTypes.string, success: PropTypes.bool, @@ -909,24 +864,6 @@ RegistrationPage.propTypes = { registrationErrorCode: PropTypes.string, submitState: PropTypes.string, thirdPartyAuthApiStatus: PropTypes.string, - registrationFormData: PropTypes.shape({ - country: PropTypes.string, - email: PropTypes.string, - name: PropTypes.string, - password: PropTypes.string, - username: PropTypes.string, - marketingOptIn: PropTypes.bool, - errors: PropTypes.shape({ - email: PropTypes.string, - name: PropTypes.string, - username: PropTypes.string, - password: PropTypes.string, - country: PropTypes.string, - }), - emailFieldBorderClass: PropTypes.string, - emailErrorSuggestion: PropTypes.string, - emailWarningSuggestion: PropTypes.string, - }), thirdPartyAuthContext: PropTypes.shape({ currentProvider: PropTypes.string, platformName: PropTypes.string, @@ -969,7 +906,6 @@ const mapStateToProps = state => { validationDecisions: validationsSelector(state), statusCode: state.register.statusCode, usernameSuggestions: usernameSuggestionsSelector(state), - registrationFormData: registrationFormDataSelector(state), fieldDescriptions: fieldDescriptionSelector(state), extendedProfile: extendedProfileSelector(state), }; @@ -983,6 +919,5 @@ export default connect( fetchRealtimeValidations, registerNewUser, resetRegistrationForm, - setRegistrationFormData, }, )(injectIntl(RegistrationPage)); diff --git a/src/register/data/actions.js b/src/register/data/actions.js index d880f5d6..002f767b 100644 --- a/src/register/data/actions.js +++ b/src/register/data/actions.js @@ -4,7 +4,6 @@ export const REGISTER_NEW_USER = new AsyncActionType('REGISTRATION', 'REGISTER_N export const REGISTER_FORM_VALIDATIONS = new AsyncActionType('REGISTRATION', 'GET_FORM_VALIDATIONS'); export const REGISTRATION_FORM = new AsyncActionType('REGISTRATION', 'REGISTRATION_FORM'); export const REGISTER_CLEAR_USERNAME_SUGGESTIONS = 'REGISTRATION_CLEAR_USERNAME_SUGGESTIONS'; -export const REGISTER_PERSIST_FORM_DATA = 'REGISTER_PERSIST_FORM_DATA'; // Reset Form export const resetRegistrationForm = () => ({ @@ -54,8 +53,3 @@ export const fetchRealtimeValidationsFailure = () => ({ export const clearUsernameSuggestions = () => ({ type: REGISTER_CLEAR_USERNAME_SUGGESTIONS, }); - -export const setRegistrationFormData = (registrationFormData) => ({ - type: REGISTER_PERSIST_FORM_DATA, - payload: { registrationFormData }, -}); diff --git a/src/register/data/reducers.js b/src/register/data/reducers.js index c1d65fa1..b53a63d7 100644 --- a/src/register/data/reducers.js +++ b/src/register/data/reducers.js @@ -3,7 +3,6 @@ import { REGISTER_NEW_USER, REGISTER_FORM_VALIDATIONS, REGISTER_CLEAR_USERNAME_SUGGESTIONS, - REGISTER_PERSIST_FORM_DATA, } from './actions'; import { @@ -14,24 +13,7 @@ import { export const defaultState = { registrationError: {}, registrationResult: {}, - registrationFormData: { - country: '', - email: '', - name: '', - password: '', - username: '', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailFieldBorderClass: '', - emailErrorSuggestion: null, - emailWarningSuggestion: null, - }, + formData: null, validations: null, statusCode: null, usernameSuggestions: [], @@ -45,8 +27,6 @@ const reducer = (state = defaultState, action) => { case REGISTRATION_FORM.RESET: return { ...defaultState, - registrationFormData: state.registrationFormData, - usernameSuggestions: state.usernameSuggestions, }; case REGISTER_NEW_USER.BEGIN: return { @@ -92,13 +72,6 @@ const reducer = (state = defaultState, action) => { ...state, usernameSuggestions: [], }; - case REGISTER_PERSIST_FORM_DATA: { - const { registrationFormData } = action.payload; - return { - ...state, - registrationFormData, - }; - } default: return state; } diff --git a/src/register/data/selectors.js b/src/register/data/selectors.js index d1118e35..52969d72 100644 --- a/src/register/data/selectors.js +++ b/src/register/data/selectors.js @@ -41,8 +41,3 @@ export const usernameSuggestionsSelector = createSelector( registerSelector, register => register.usernameSuggestions, ); - -export const registrationFormDataSelector = createSelector( - registerSelector, - register => register.registrationFormData, -); diff --git a/src/register/data/tests/reducers.test.js b/src/register/data/tests/reducers.test.js index 3c715a59..a92acc07 100644 --- a/src/register/data/tests/reducers.test.js +++ b/src/register/data/tests/reducers.test.js @@ -1,10 +1,6 @@ import reducer from '../reducers'; import { - REGISTER_CLEAR_USERNAME_SUGGESTIONS, - REGISTER_FORM_VALIDATIONS, - REGISTER_NEW_USER, - REGISTER_PERSIST_FORM_DATA, - REGISTRATION_FORM, + REGISTER_CLEAR_USERNAME_SUGGESTIONS, REGISTER_FORM_VALIDATIONS, REGISTER_NEW_USER, } from '../actions'; import { DEFAULT_STATE } from '../../../data/constants'; @@ -14,24 +10,7 @@ describe('register reducer', () => { { registrationError: {}, registrationResult: {}, - registrationFormData: { - country: '', - email: '', - name: '', - password: '', - username: '', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailFieldBorderClass: '', - emailErrorSuggestion: null, - emailWarningSuggestion: null, - }, + formData: null, validations: null, statusCode: null, usernameSuggestions: [], @@ -96,93 +75,4 @@ describe('register reducer', () => { }, ); }); - it('should not reset username suggestions and form data in form reset', () => { - const state = { - registrationError: {}, - registrationResult: {}, - registrationFormData: { - country: 'Pakistan', - email: 'test@email.com', - name: 'John Doe', - password: 'johndoe', - username: 'john', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailErrorSuggestion: 'test@email.com', - emailWarningSuggestion: 'test@email.com', - }, - validations: null, - statusCode: null, - extendedProfile: [], - fieldDescriptions: {}, - formRenderState: DEFAULT_STATE, - usernameSuggestions: ['test1', 'test2'], - }; - const action = { - type: REGISTRATION_FORM.RESET, - }; - - expect( - reducer(state, action), - ).toEqual( - state, - ); - }); - - it('should set registrationFormData', () => { - const state = { - registrationFormData: { - country: '', - email: '', - name: '', - password: '', - username: '', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailErrorSuggestion: null, - emailWarningSuggestion: null, - }, - }; - const registrationFormData = { - country: 'Pakistan', - email: 'test@email.com', - name: 'John Doe', - password: 'johndoe', - username: 'john', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailErrorSuggestion: 'test@email.com', - emailWarningSuggestion: 'test@email.com', - }; - const action = { - type: REGISTER_PERSIST_FORM_DATA, - payload: { registrationFormData }, - }; - - expect( - reducer(state, action), - ).toEqual( - { - registrationFormData, - }, - ); - }); }); diff --git a/src/register/data/tests/sagas.test.js b/src/register/data/tests/sagas.test.js index d0014bf2..9fb7e448 100644 --- a/src/register/data/tests/sagas.test.js +++ b/src/register/data/tests/sagas.test.js @@ -15,7 +15,7 @@ const { loggingService } = initializeMockLogging(); describe('fetchRealtimeValidations', () => { const params = { payload: { - registrationFormData: { + formData: { email: 'test@test.com', username: '', password: 'test-password', @@ -112,7 +112,7 @@ describe('fetchRealtimeValidations', () => { describe('handleNewUserRegistration', () => { const params = { payload: { - registrationFormData: { + formData: { email: 'test@test.com', username: 'test-username', password: 'test-password', diff --git a/src/register/tests/RegistrationPage.test.jsx b/src/register/tests/RegistrationPage.test.jsx index d1fb65d7..b223f02e 100644 --- a/src/register/tests/RegistrationPage.test.jsx +++ b/src/register/tests/RegistrationPage.test.jsx @@ -13,7 +13,7 @@ import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n import { clearUsernameSuggestions, fetchRealtimeValidations, - registerNewUser, setRegistrationFormData, + registerNewUser, resetRegistrationForm, } from '../data/actions'; import { @@ -43,7 +43,6 @@ describe('RegistrationPage', () => { let props = {}; let store = {}; - let registrationFormData = {}; const reduxWrapper = children => ( @@ -87,24 +86,6 @@ describe('RegistrationPage', () => { handleInstitutionLogin: jest.fn(), institutionLogin: false, }; - registrationFormData = { - country: '', - email: '', - name: '', - password: '', - username: '', - marketingOptIn: true, - errors: { - email: '', - name: '', - username: '', - password: '', - country: '', - }, - emailFieldBorderClass: '', - emailErrorSuggestion: null, - emailWarningSuggestion: null, - }; }); afterEach(() => { @@ -271,6 +252,19 @@ describe('RegistrationPage', () => { expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload)); }); + it('should validate the did you mean suggestions', () => { + const registrationPage = mount(reduxWrapper()); + + registrationPage.find('input#email').simulate('blur', { target: { value: 'test@gmail.con', name: 'email' } }); + expect(registrationPage.find('RegistrationPage').state('emailErrorSuggestion')).toEqual('test@gmail.com'); + + registrationPage.find('input#email').simulate('blur', { target: { value: 'test@fmail.com', name: 'email' } }); + expect(registrationPage.find('RegistrationPage').state('emailWarningSuggestion')).toEqual('test@gmail.com'); + + registrationPage.find('input#email').simulate('blur', { target: { value: 'test@hotmail.com', name: 'email' } }); + expect(registrationPage.find('RegistrationPage').state('emailWarningSuggestion')).toEqual(null); + }); + it('should update props with validations returned by registration api', () => { store = mockStore({ ...initialState, @@ -517,6 +511,8 @@ describe('RegistrationPage', () => { registerPage.find('RegistrationPage').instance().shouldComponentUpdate(props); registerPage.find('RegistrationPage').setState({ errors: { username: 'It looks like this username is already taken' } }); expect(registerPage.find('button.username-suggestion').length).toEqual(3); + registerPage.find('button.username-suggestion').at(0).simulate('click'); + expect(registerPage.find('RegistrationPage').state('username')).toEqual('test_1'); }); it('should show username suggestions when full name is populated', () => { @@ -532,6 +528,8 @@ describe('RegistrationPage', () => { registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); expect(registerPage.find('button.username-suggestion').length).toEqual(3); + registerPage.find('button.username-suggestion').at(0).simulate('click'); + expect(registerPage.find('RegistrationPage').state('username')).toEqual('testname'); }); it('should clear username suggestions when close icon is clicked', () => { @@ -764,30 +762,6 @@ describe('RegistrationPage', () => { expect(registrationPage.find('RegistrationPage').state('errorCode')).toEqual(INTERNAL_SERVER_ERROR); }); - it('should update form fields state if updated in redux store', () => { - const nextProps = { - thirdPartyAuthContext, - registrationFormData: { - name: 'John Doe', - username: 'john_doe', - email: 'john.doe@example.com', - password: 'password1', - country: 'Pakistan', - emailErrorSuggestion: 'test@gmail.com', - }, - }; - - const registrationPage = mount(reduxWrapper()); - registrationPage.find('RegistrationPage').instance().shouldComponentUpdate(nextProps); - - expect(registrationPage.find('RegistrationPage').state('name')).toEqual('John Doe'); - expect(registrationPage.find('RegistrationPage').state('username')).toEqual('john_doe'); - expect(registrationPage.find('RegistrationPage').state('email')).toEqual('john.doe@example.com'); - expect(registrationPage.find('RegistrationPage').state('emailErrorSuggestion')).toEqual('test@gmail.com'); - expect(registrationPage.find('RegistrationPage').state('password')).toEqual('password1'); - expect(registrationPage.find('RegistrationPage').state('country')).toEqual('Pakistan'); - }); - it('should display opt-in/opt-out checkbox', () => { mergeConfig({ MARKETING_EMAILS_OPT_IN: 'true', @@ -800,115 +774,6 @@ describe('RegistrationPage', () => { MARKETING_EMAILS_OPT_IN: '', }); }); - - // ******** persist state tests ******** - - it('should clear form field errors in redux store on onFocus', () => { - store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#name').simulate('focus'); - expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData)); - }); - - it('should set username in redux store if usernameSuggestion is clicked', () => { - registrationFormData = { - ...registrationFormData, - username: 'testname', - }; - store = mockStore({ - ...initialState, - register: { - ...initialState.register, - usernameSuggestions: ['testname', 't.name', 'test_0'], - }, - }); - store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); - registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); - registerPage.find('button.username-suggestion').at(0).simulate('click'); - expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData)); - }); - - it('should set email in redux store if emailSuggestion is clicked', () => { - registrationFormData = { - ...registrationFormData, - email: 'test@gmail.com', - }; - store = mockStore({ - ...initialState, - register: { - ...initialState.register, - }, - }); - store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#email').simulate('blur', { target: { value: 'test@gmail.con', name: 'email' } }); - registrationPage.find('RegistrationPage').setState({ emailErrorSuggestion: 'test@gmail.com' }); - registrationPage.find('.alert-link').simulate('click'); - expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData)); - }); - - it('should clear username in redux store if usernameSuggestion close button is clicked', () => { - store = mockStore({ - ...initialState, - register: { - ...initialState.register, - usernameSuggestions: ['testname', 't.name', 'test_0'], - }, - }); - store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); - registerPage.find('input#name').simulate('change', { target: { value: 'test name', name: 'name' } }); - registerPage.find('button.suggested-username-close-button').simulate('click'); - expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData)); - }); - - it('should clear emailErrorSuggestion in redux store if close button is clicked', () => { - registrationFormData = { - ...registrationFormData, - email: '', - }; - store = mockStore({ - ...initialState, - register: { - ...initialState.register, - }, - }); - store.dispatch = jest.fn(store.dispatch); - const registrationPage = mount(reduxWrapper()); - registrationPage.find('input#email').simulate('blur', { target: { value: 'test@gmail.con', name: 'email' } }); - registrationPage.find('RegistrationPage').setState({ emailErrorSuggestion: 'test@gmail.com' }); - registrationPage.find('.alert-close').at(0).simulate('click'); - expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData)); - }); - - it('should set errors in redux store if form payload is invalid', () => { - registrationFormData = { - ...registrationFormData, - errors: { - ...registrationFormData.errors, - name: 'Enter your full name', - }, - }; - - const payload = { - name: '', - username: 'john_doe', - email: 'john.doe@example.com', - password: 'password1', - country: 'Pakistan', - honor_code: true, - totalRegistrationTime: 0, - is_authn_mfe: true, - }; - - store.dispatch = jest.fn(store.dispatch); - const registerPage = mount(reduxWrapper()); - - populateRequiredFields(registerPage, payload); - registerPage.find('button.btn-brand').simulate('click'); - expect(store.dispatch).toHaveBeenCalledWith(setRegistrationFormData(registrationFormData)); - }); }); describe('TestDynamicFields', () => {