diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index d438f79d..cdc14a8c 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -4,7 +4,7 @@ import snakeCase from 'lodash.snakecase'; import { connect } from 'react-redux'; import Skeleton from 'react-loading-skeleton'; import { Helmet } from 'react-helmet'; -import PropTypes from 'prop-types'; +import PropTypes, { string } from 'prop-types'; import { getConfig } from '@edx/frontend-platform'; import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; @@ -18,9 +18,13 @@ import { import { ExpandMore } from '@edx/paragon/icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { registerNewUser, resetRegistrationForm, fetchRealtimeValidations } from './data/actions'; +import { + clearUsernameSuggestions, registerNewUser, resetRegistrationForm, fetchRealtimeValidations, +} from './data/actions'; import { FORM_SUBMISSION_ERROR } from './data/constants'; -import { registrationErrorSelector, registrationRequestSelector, validationsSelector } from './data/selectors'; +import { + registrationErrorSelector, registrationRequestSelector, validationsSelector, usernameSuggestionsSelector, +} from './data/selectors'; import messages from './messages'; import OptionalFields from './OptionalFields'; import RegistrationFailure from './RegistrationFailure'; @@ -221,16 +225,13 @@ class RegistrationPage extends React.Component { } handleSuggestionClick = (suggestion) => { - const fieldName = 'username'; - const payload = { - is_authn_mfe: true, - username: suggestion, - }; + const { errors } = this.state; + errors.username = ''; this.setState({ - ...payload, - }, () => { - this.validateInput(fieldName, suggestion, payload); + username: suggestion, + errors, }); + this.props.clearUsernameSuggestions(); } isFormValid(validations) { @@ -407,6 +408,7 @@ class RegistrationPage extends React.Component { helpText={[intl.formatMessage(messages['help.text.username.1']), intl.formatMessage(messages['help.text.username.2'])]} floatingLabel={intl.formatMessage(messages['registration.username.label'])} handleSuggestionClick={this.handleSuggestionClick} + usernameSuggestions={this.props.usernameSuggestions} /> { @@ -610,6 +615,7 @@ const mapStateToProps = state => { thirdPartyAuthContext, validationDecisions: validationsSelector(state), statusCode: state.register.statusCode, + usernameSuggestions: usernameSuggestionsSelector(state), }; }; @@ -620,5 +626,6 @@ export default connect( fetchRealtimeValidations, registerNewUser, resetRegistrationForm, + clearUsernameSuggestions, }, )(injectIntl(RegistrationPage)); diff --git a/src/register/UsernameField.jsx b/src/register/UsernameField.jsx index 255fb017..a0a4c808 100644 --- a/src/register/UsernameField.jsx +++ b/src/register/UsernameField.jsx @@ -1,11 +1,9 @@ import React from 'react'; import PropTypes, { string } from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { connect } from 'react-redux'; import { Button } from '@edx/paragon'; -import { usernameSuggestionsSelector } from './data/selectors'; import { FormGroup } from '../common-components'; import messages from './messages'; @@ -49,10 +47,4 @@ UsernameField.propTypes = { value: PropTypes.string.isRequired, }; -const mapStateToProps = state => ({ - usernameSuggestions: usernameSuggestionsSelector(state), -}); - -export default connect( - mapStateToProps, -)(injectIntl(UsernameField)); +export default injectIntl(UsernameField); diff --git a/src/register/data/actions.js b/src/register/data/actions.js index 1942b3cf..002f767b 100644 --- a/src/register/data/actions.js +++ b/src/register/data/actions.js @@ -3,6 +3,7 @@ import { AsyncActionType } from '../../data/utils'; export const REGISTER_NEW_USER = new AsyncActionType('REGISTRATION', 'REGISTER_NEW_USER'); 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'; // Reset Form export const resetRegistrationForm = () => ({ @@ -48,3 +49,7 @@ export const fetchRealtimeValidationsSuccess = (validations) => ({ export const fetchRealtimeValidationsFailure = () => ({ type: REGISTER_FORM_VALIDATIONS.FAILURE, }); + +export const clearUsernameSuggestions = () => ({ + type: REGISTER_CLEAR_USERNAME_SUGGESTIONS, +}); diff --git a/src/register/data/reducers.js b/src/register/data/reducers.js index 67a8fde7..813c6156 100644 --- a/src/register/data/reducers.js +++ b/src/register/data/reducers.js @@ -1,4 +1,6 @@ -import { REGISTRATION_FORM, REGISTER_NEW_USER, REGISTER_FORM_VALIDATIONS } from './actions'; +import { + REGISTRATION_FORM, REGISTER_NEW_USER, REGISTER_FORM_VALIDATIONS, REGISTER_CLEAR_USERNAME_SUGGESTIONS, +} from './actions'; import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants'; @@ -8,6 +10,7 @@ export const defaultState = { formData: null, validations: null, statusCode: null, + usernameSuggestions: [], }; const reducer = (state = defaultState, action) => { @@ -27,28 +30,39 @@ const reducer = (state = defaultState, action) => { ...state, registrationResult: action.payload, }; - case REGISTER_NEW_USER.FAILURE: + case REGISTER_NEW_USER.FAILURE: { + const { usernameSuggestions } = action.payload; return { ...state, registrationError: { ...action.payload }, submitState: DEFAULT_STATE, validations: null, + usernameSuggestions: usernameSuggestions || state.usernameSuggestions, }; + } case REGISTER_FORM_VALIDATIONS.BEGIN: return { ...state, }; - case REGISTER_FORM_VALIDATIONS.SUCCESS: + case REGISTER_FORM_VALIDATIONS.SUCCESS: { + const { usernameSuggestions } = action.payload.validations; return { ...state, validations: action.payload.validations, + usernameSuggestions: usernameSuggestions || state.usernameSuggestions, }; + } case REGISTER_FORM_VALIDATIONS.FAILURE: return { ...state, statusCode: 403, validations: null, }; + case REGISTER_CLEAR_USERNAME_SUGGESTIONS: + return { + ...state, + usernameSuggestions: [], + }; default: return state; } diff --git a/src/register/data/selectors.js b/src/register/data/selectors.js index 5a1b6efe..52969d72 100644 --- a/src/register/data/selectors.js +++ b/src/register/data/selectors.js @@ -39,15 +39,5 @@ export const validationsSelector = createSelector( export const usernameSuggestionsSelector = createSelector( registerSelector, - (register) => { - const { registrationError, validations } = register; - let usernameSuggestions = validations && validations.usernameSuggestions ? validations.usernameSuggestions : []; - if (usernameSuggestions.length === 0) { - usernameSuggestions = ( - registrationError && registrationError.usernameSuggestions ? registrationError.usernameSuggestions : [] - ); - } - - return usernameSuggestions; - }, + register => register.usernameSuggestions, ); diff --git a/src/register/data/tests/reducers.test.js b/src/register/data/tests/reducers.test.js new file mode 100644 index 00000000..2fdae6ae --- /dev/null +++ b/src/register/data/tests/reducers.test.js @@ -0,0 +1,75 @@ +import reducer from '../reducers'; +import { + REGISTER_CLEAR_USERNAME_SUGGESTIONS, REGISTER_FORM_VALIDATIONS, REGISTER_NEW_USER, +} from '../actions'; +import { DEFAULT_STATE } from '../../../data/constants'; + +describe('register reducer', () => { + it('should return the initial state', () => { + expect(reducer(undefined, {})).toEqual( + { + registrationError: {}, + registrationResult: {}, + formData: null, + validations: null, + statusCode: null, + usernameSuggestions: [], + }, + ); + }); + + it('should set username suggestions upon validation failed case', () => { + const state = { usernameSuggestions: [] }; + const validations = { usernameSuggestions: ['test12'] }; + const action = { + type: REGISTER_FORM_VALIDATIONS.SUCCESS, + payload: { validations }, + }; + + expect( + reducer(state, action), + ).toEqual( + { + validations, + usernameSuggestions: validations.usernameSuggestions, + }, + ); + }); + + it('should set username suggestions upon registration error case', () => { + const state = { usernameSuggestions: [] }; + const payload = { usernameSuggestions: ['test12'] }; + const action = { + type: REGISTER_NEW_USER.FAILURE, + payload, + }; + + expect( + reducer(state, action), + ).toEqual( + { + registrationError: payload, + submitState: DEFAULT_STATE, + usernameSuggestions: payload.usernameSuggestions, + validations: null, + }, + ); + }); + + it('should clear username suggestions from validations state', () => { + const state = { + usernameSuggestions: ['test_1'], + }; + const action = { + type: REGISTER_CLEAR_USERNAME_SUGGESTIONS, + }; + + expect( + reducer(state, action), + ).toEqual( + { + usernameSuggestions: [], + }, + ); + }); +});