Merge pull request #121 from edx/aehsan/van-307/added_field_errors_on_submit
Added fields errors in case of submit
This commit is contained in:
@@ -48,7 +48,7 @@ exports[`ForgotPasswordPage should match default section snapshot 1`] = `
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
@@ -169,7 +169,7 @@ exports[`ForgotPasswordPage should match forbidden section snapshot 1`] = `
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
@@ -275,7 +275,7 @@ exports[`ForgotPasswordPage should match pending section snapshot 1`] = `
|
||||
</small>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
|
||||
@@ -70,7 +70,7 @@ const LoginHelpLinks = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" className="mt-1 field-link small" onClick={toggleLoginHelp}>
|
||||
<button type="button" className="mt-2 field-link small" onClick={toggleLoginHelp}>
|
||||
<FontAwesomeIcon className="mr-1" icon={showLoginHelp ? faCaretDown : faCaretRight} />
|
||||
{getHelpButtonMessage()}
|
||||
</button>
|
||||
|
||||
@@ -95,7 +95,7 @@ exports[`LoginPage should match TPA provider snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
@@ -293,7 +293,7 @@ exports[`LoginPage should match default section snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
@@ -493,7 +493,7 @@ exports[`LoginPage should match forget password alert message snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
@@ -655,7 +655,7 @@ exports[`LoginPage should match pending button state snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
@@ -854,7 +854,7 @@ exports[`LoginPage should show error message 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<button
|
||||
className="mt-1 field-link small"
|
||||
className="mt-2 field-link small"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
|
||||
@@ -96,6 +96,7 @@ class RegistrationPage extends React.Component {
|
||||
institutionLogin: false,
|
||||
formValid: false,
|
||||
submitCount: 0,
|
||||
isSubmitted: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -146,6 +147,7 @@ class RegistrationPage extends React.Component {
|
||||
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
this.state.isSubmitted = true;
|
||||
const params = (new URL(document.location)).searchParams;
|
||||
const payload = {};
|
||||
const payloadMap = new Map();
|
||||
@@ -205,14 +207,14 @@ class RegistrationPage extends React.Component {
|
||||
}
|
||||
|
||||
handleOnBlur(e) {
|
||||
this.state.isSubmitted = false;
|
||||
if (this.props.statusCode === 403) {
|
||||
this.validateInput(e.target.name, e.target.value);
|
||||
this.validateInput(e.target.name, e.target.value, 'blurCase');
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
validationFieldName: e.target.name,
|
||||
});
|
||||
|
||||
const payload = {
|
||||
email: this.state.email,
|
||||
username: this.state.username,
|
||||
@@ -241,8 +243,8 @@ class RegistrationPage extends React.Component {
|
||||
}
|
||||
|
||||
handleOnClick(e) {
|
||||
const { errors } = this.state;
|
||||
if (this.state.currentValidations) {
|
||||
if (this.state.currentValidations && this.props.statusCode !== 403) {
|
||||
const { errors } = this.state;
|
||||
const fieldName = e.target.name;
|
||||
errors[fieldName] = this.state.currentValidations[fieldName];
|
||||
const stateValidKey = `${camelCase(fieldName)}Valid`;
|
||||
@@ -257,7 +259,7 @@ class RegistrationPage extends React.Component {
|
||||
sendTrackEvent('edx.bi.login_form.toggled', { category: 'user-engagement' });
|
||||
}
|
||||
|
||||
validateInput(inputName, value) {
|
||||
validateInput(inputName, value, blurCase) {
|
||||
const {
|
||||
errors,
|
||||
validationErrorsAlertMessages,
|
||||
@@ -277,14 +279,17 @@ class RegistrationPage extends React.Component {
|
||||
validationErrorsAlertMessages.email = [{ user_message: validations.email }];
|
||||
} else if (value.length < 1) {
|
||||
const errorEmpty = this.generateUserMessage(value.length < 1, 'email.validation.message');
|
||||
validationErrorsAlertMessages.email = errorEmpty;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.email = errorEmpty;
|
||||
}
|
||||
errors.email = errorEmpty[0].user_message;
|
||||
} else {
|
||||
const errorCharlength = this.generateUserMessage(value.length <= 2, 'email.ratelimit.less.chars.validation.message');
|
||||
const formatError = this.generateUserMessage(!value.match(/^([\w.%+-]+)@([\w-]+\.)+([\w]{2,})$/i), 'email.ratelimit.incorrect.format.validation.message');
|
||||
validationErrorsAlertMessages.email = errorCharlength;
|
||||
validationErrorsAlertMessages.emailFormat = formatError;
|
||||
errors.email = errorCharlength[0].user_message;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.email = value.length <= 2 ? errorCharlength : formatError;
|
||||
}
|
||||
errors.email = value.length <= 2 ? errorCharlength[0].user_message : formatError[0].user_message;
|
||||
}
|
||||
break;
|
||||
case 'name':
|
||||
@@ -292,8 +297,10 @@ class RegistrationPage extends React.Component {
|
||||
validationErrorsAlertMessages.name = [{ user_message: validations.name }];
|
||||
} else if (value.length < 1) {
|
||||
const errorEmpty = this.generateUserMessage(value.length < 1, 'fullname.validation.message');
|
||||
validationErrorsAlertMessages.name = errorEmpty;
|
||||
errors.name = validationErrorsAlertMessages.name[0].user_message;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.name = errorEmpty;
|
||||
}
|
||||
errors.name = errorEmpty[0].user_message;
|
||||
} else {
|
||||
validationErrorsAlertMessages.name = [{ user_message: '' }];
|
||||
errors.name = validationErrorsAlertMessages.name[0].user_message;
|
||||
@@ -304,12 +311,22 @@ class RegistrationPage extends React.Component {
|
||||
validationErrorsAlertMessages.username = [{ user_message: validations.username }];
|
||||
} else if (value.length < 1) {
|
||||
const errorEmpty = this.generateUserMessage(value.length < 1, 'username.validation.message');
|
||||
validationErrorsAlertMessages.username = errorEmpty;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.username = errorEmpty;
|
||||
}
|
||||
errors.username = errorEmpty[0].user_message;
|
||||
} else {
|
||||
} else if (value.length <= 1) {
|
||||
const errorCharLength = this.generateUserMessage(value.length <= 1, 'username.ratelimit.less.chars.message');
|
||||
validationErrorsAlertMessages.username = errorCharLength;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.username = errorCharLength;
|
||||
}
|
||||
errors.username = errorCharLength[0].user_message;
|
||||
} else if (!value.match(/^([a-zA-Z0-9_-])$/i)) {
|
||||
const formatError = this.generateUserMessage(!value.match(/^[a-zA-Z0-9_-]*$/i), 'username.format.validation.message');
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.username = formatError;
|
||||
}
|
||||
errors.username = formatError[0].user_message;
|
||||
}
|
||||
break;
|
||||
case 'password':
|
||||
@@ -317,12 +334,28 @@ class RegistrationPage extends React.Component {
|
||||
validationErrorsAlertMessages.password = [{ user_message: validations.password }];
|
||||
} else if (value.length < 1) {
|
||||
const errorEmpty = this.generateUserMessage(value.length < 1, 'register.page.password.validation.message');
|
||||
validationErrorsAlertMessages.password = errorEmpty;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.password = errorEmpty;
|
||||
}
|
||||
errors.password = errorEmpty[0].user_message;
|
||||
} else if (value.length < 8) {
|
||||
const errorCharlength = this.generateUserMessage(value.length < 8, 'email.ratelimit.password.validation.message');
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.password = errorCharlength;
|
||||
}
|
||||
errors.password = errorCharlength[0].user_message;
|
||||
} else if (!value.match(/.*[0-9].*/i)) {
|
||||
const formatError = this.generateUserMessage(!value.match(/.*[0-9].*/i), 'username.number.validation.message');
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.password = formatError;
|
||||
}
|
||||
errors.password = formatError[0].user_message;
|
||||
} else {
|
||||
const errorCharLength = this.generateUserMessage(value.length < 8, 'email.ratelimit.password.validation.message');
|
||||
validationErrorsAlertMessages.password = errorCharLength;
|
||||
errors.password = errorCharLength[0].user_message;
|
||||
const formatError = this.generateUserMessage(!value.match(/.*[a-zA-Z].*/i), 'username.character.validation.message');
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.password = formatError;
|
||||
}
|
||||
errors.password = formatError[0].user_message;
|
||||
}
|
||||
break;
|
||||
case 'country':
|
||||
@@ -330,7 +363,9 @@ class RegistrationPage extends React.Component {
|
||||
validationErrorsAlertMessages.country = [{ user_message: validations.country }];
|
||||
} else {
|
||||
const emptyError = this.generateUserMessage(value === '', 'country.validation.message');
|
||||
validationErrorsAlertMessages.country = emptyError;
|
||||
if (blurCase === true) {
|
||||
validationErrorsAlertMessages.country = emptyError;
|
||||
}
|
||||
errors.country = emptyError[0].user_message;
|
||||
}
|
||||
break;
|
||||
@@ -346,7 +381,9 @@ class RegistrationPage extends React.Component {
|
||||
break;
|
||||
}
|
||||
|
||||
submitCount++;
|
||||
if (!blurCase) {
|
||||
submitCount++;
|
||||
}
|
||||
formValid = this.checkNoValidationsErrors(validationErrorsAlertMessages);
|
||||
this.setState({
|
||||
formValid,
|
||||
@@ -510,6 +547,43 @@ class RegistrationPage extends React.Component {
|
||||
return [{ user_message: isFieldInValid ? this.intl.formatMessage(messages[messageID]) : '' }];
|
||||
}
|
||||
|
||||
updateFieldErrors(errorMessages) {
|
||||
const {
|
||||
errors,
|
||||
} = this.state;
|
||||
if (errorMessages.email) {
|
||||
errors.email = errorMessages.email[0].user_message;
|
||||
}
|
||||
if (errorMessages.username) {
|
||||
errors.username = errorMessages.username[0].user_message;
|
||||
}
|
||||
if (errorMessages.name) {
|
||||
errors.name = errorMessages.name[0].user_message;
|
||||
}
|
||||
if (errorMessages.password) {
|
||||
errors.password = errorMessages.password[0].user_message;
|
||||
}
|
||||
if (errorMessages.country) {
|
||||
errors.country = errorMessages.country[0].user_message;
|
||||
}
|
||||
}
|
||||
|
||||
renderErrors() {
|
||||
let errorsObject = null;
|
||||
if (!this.checkNoValidationsErrors(this.state.validationErrorsAlertMessages)) {
|
||||
this.updateFieldErrors(this.state.validationErrorsAlertMessages);
|
||||
errorsObject = this.state.validationErrorsAlertMessages;
|
||||
} else if (this.props.registrationError) {
|
||||
if (this.state.isSubmitted) {
|
||||
this.updateFieldErrors(this.props.registrationError);
|
||||
}
|
||||
errorsObject = this.props.registrationError;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return <RegistrationFailure errors={errorsObject} submitCount={this.state.submitCount} />;
|
||||
}
|
||||
|
||||
renderThirdPartyAuth(providers, secondaryProviders, currentProvider, thirdPartyAuthApiStatus, intl) {
|
||||
let thirdPartyComponent = null;
|
||||
if ((providers.length || secondaryProviders.length) && !currentProvider) {
|
||||
@@ -531,18 +605,6 @@ class RegistrationPage extends React.Component {
|
||||
return thirdPartyComponent;
|
||||
}
|
||||
|
||||
renderErrors() {
|
||||
let errorsObject = null;
|
||||
if (!this.checkNoValidationsErrors(this.state.validationErrorsAlertMessages)) {
|
||||
errorsObject = this.state.validationErrorsAlertMessages;
|
||||
} else if (this.props.registrationError) {
|
||||
errorsObject = this.props.registrationError;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return <RegistrationFailure errors={errorsObject} submitCount={this.state.submitCount} />;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, submitState, thirdPartyAuthApiStatus } = this.props;
|
||||
const {
|
||||
@@ -758,6 +820,9 @@ RegistrationPage.propTypes = {
|
||||
registrationError: PropTypes.shape({
|
||||
email: PropTypes.array,
|
||||
username: PropTypes.array,
|
||||
country: PropTypes.array,
|
||||
password: PropTypes.array,
|
||||
name: PropTypes.array,
|
||||
}),
|
||||
submitState: PropTypes.string,
|
||||
thirdPartyAuthApiStatus: PropTypes.string,
|
||||
|
||||
@@ -96,6 +96,21 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Please enter your Public Username.',
|
||||
description: 'Validation message that appears when username is invalid',
|
||||
},
|
||||
'username.format.validation.message': {
|
||||
id: 'username.format.validation.message',
|
||||
defaultMessage: 'Usernames can only contain letters (A-Z, a-z), numerals (0-9), underscores (_), and hyphens (-).',
|
||||
description: 'Validation message that appears when username format is invalid',
|
||||
},
|
||||
'username.character.validation.message': {
|
||||
id: 'username.character.validation.message',
|
||||
defaultMessage: 'Your password must contain at least 1 letter.',
|
||||
description: 'Validation message that appears when password does not contain letter',
|
||||
},
|
||||
'username.number.validation.message': {
|
||||
id: 'username.number.validation.message',
|
||||
defaultMessage: 'Your password must contain at least 1 number.',
|
||||
description: 'Validation message that appears when password does not contain number',
|
||||
},
|
||||
'username.ratelimit.less.chars.message': {
|
||||
id: 'username.ratelimit.less.chars.message',
|
||||
defaultMessage: 'Public Username must have atleast 2 characters.',
|
||||
|
||||
@@ -257,6 +257,63 @@ describe('./RegistrationPage.js', () => {
|
||||
expect(store.dispatch).toHaveBeenCalledWith(fetchRealtimeValidations(formPayload));
|
||||
});
|
||||
|
||||
it('should call Validations function on Blur in case of 403', () => {
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
register: {
|
||||
...initialState.register,
|
||||
statusCode: 403,
|
||||
},
|
||||
});
|
||||
const errors = {
|
||||
email: 'Please enter your Email.',
|
||||
name: 'Please enter your Full Name.',
|
||||
username: 'Please enter your Public Username.',
|
||||
password: 'Please enter your Password.',
|
||||
country: '',
|
||||
honorCode: '',
|
||||
termsOfService: '',
|
||||
};
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('input#username').simulate('blur', { target: { value: '', name: 'username' } });
|
||||
registrationPage.find('input#name').simulate('blur', { target: { value: '', name: 'name' } });
|
||||
registrationPage.find('input#email').simulate('blur', { target: { value: '', name: 'email' } });
|
||||
registrationPage.find('input#password').simulate('blur', { target: { value: '', name: 'password' } });
|
||||
expect(registrationPage.find('RegistrationPage').state('errors')).toEqual(errors);
|
||||
});
|
||||
|
||||
it('validate passwrod validations incsae of 403', () => {
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
register: {
|
||||
...initialState.register,
|
||||
statusCode: 403,
|
||||
},
|
||||
});
|
||||
const errors = {
|
||||
email: '',
|
||||
name: '',
|
||||
username: '',
|
||||
password: 'Your password must contain at least 8 characters',
|
||||
country: '',
|
||||
honorCode: '',
|
||||
termsOfService: '',
|
||||
};
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('input#password').simulate('blur', { target: { value: 'pas', name: 'password' } });
|
||||
expect(registrationPage.find('RegistrationPage').state('errors')).toEqual(errors);
|
||||
|
||||
errors.password = 'Your password must contain at least 1 number.';
|
||||
registrationPage.find('input#password').simulate('blur', { target: { value: 'passwordd', name: 'password' } });
|
||||
expect(registrationPage.find('RegistrationPage').state('errors')).toEqual(errors);
|
||||
|
||||
errors.password = 'Your password must contain at least 1 letter.';
|
||||
registrationPage.find('input#password').simulate('blur', { target: { value: '123456789', name: 'password' } });
|
||||
expect(registrationPage.find('RegistrationPage').state('errors')).toEqual(errors);
|
||||
});
|
||||
|
||||
it('tests shouldComponentUpdate change validations and formValid state', () => {
|
||||
const nextProps = {
|
||||
validations: {
|
||||
@@ -544,18 +601,18 @@ describe('./RegistrationPage.js', () => {
|
||||
expect(registerPage.find(<CookiePolicyBanner />)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show error message on 409', () => {
|
||||
it('should show error message on 409 on alert and below the fields', () => {
|
||||
const windowSpy = jest.spyOn(global, 'window', 'get');
|
||||
windowSpy.mockImplementation(() => ({
|
||||
scrollTo: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
}));
|
||||
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
register: {
|
||||
...initialState.register,
|
||||
isSubmitted: true,
|
||||
registrationError: {
|
||||
email: [
|
||||
{
|
||||
@@ -571,8 +628,8 @@ describe('./RegistrationPage.js', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const tree = renderer.create(reduxWrapper(<IntlRegistrationPage {...props} />)).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
const tree = renderer.create(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(tree.toJSON()).toMatchSnapshot();
|
||||
windowSpy.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -944,7 +944,7 @@ exports[`./RegistrationPage.js should match pending button state snapshot 1`] =
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`./RegistrationPage.js should show error message on 409 1`] = `
|
||||
exports[`./RegistrationPage.js should show error message on 409 on alert and below the fields 1`] = `
|
||||
<div
|
||||
className="d-flex justify-content-center m-4"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user