Merge pull request #120 from edx/adeel/van_325_form_fields_ux_feedback
Form fields ux feedback
This commit is contained in:
148
src/common-components/AuthnValidationFormGroup.jsx
Normal file
148
src/common-components/AuthnValidationFormGroup.jsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
ValidationFormGroup,
|
||||
} from '@edx/paragon';
|
||||
|
||||
const AuthnCustomValidationFormGroup = (props) => {
|
||||
const { onClick, onChange, onBlur } = props;
|
||||
const [showHelpText, setShowHelpText] = useState(false);
|
||||
const [showLabelText, setShowLabelText] = useState(false);
|
||||
const [showPlaceholder, setShowPlaceHolder] = useState(true);
|
||||
|
||||
// handler code that need to be invoked via input
|
||||
const onClickHandler = (e, clickCb) => {
|
||||
setShowHelpText(true);
|
||||
setShowLabelText(true);
|
||||
setShowPlaceHolder(false);
|
||||
if (clickCb) {
|
||||
clickCb(e);
|
||||
}
|
||||
};
|
||||
const onBlurHandler = (e, blurCb) => {
|
||||
setShowHelpText(false);
|
||||
setShowLabelText(false);
|
||||
setShowPlaceHolder(true);
|
||||
if (blurCb) {
|
||||
blurCb(e);
|
||||
}
|
||||
};
|
||||
const onChangeHandler = (e, changeCb) => {
|
||||
if (changeCb) {
|
||||
changeCb(e);
|
||||
}
|
||||
};
|
||||
const onOptionalHandler = (e, clickCb) => { clickCb(e); };
|
||||
|
||||
const showLabel = () => {
|
||||
const fieldLabel = (!props.optionalFieldCheckbox && showLabelText) ? (
|
||||
<Form.Label htmlFor={props.for} className="h6 pt-10">
|
||||
{props.label}
|
||||
</Form.Label>
|
||||
) : <span />;
|
||||
return fieldLabel;
|
||||
};
|
||||
const showOptional = () => {
|
||||
const additionalField = props.optionalFieldCheckbox ? (
|
||||
<p role="presentation" id="additionalFields" className="mb-0 small" onClick={(e) => onOptionalHandler(e, onClick)}>
|
||||
{props.checkboxMessage}
|
||||
</p>
|
||||
) : <span />;
|
||||
return additionalField;
|
||||
};
|
||||
|
||||
const inputProps = {
|
||||
name: props.name,
|
||||
id: props.for,
|
||||
type: props.type,
|
||||
value: props.value,
|
||||
};
|
||||
inputProps.placeholder = showPlaceholder ? props.label : '';
|
||||
inputProps.onChange = (e) => onChangeHandler(e, onChange);
|
||||
inputProps.onClick = (e) => onClickHandler(e, onClick);
|
||||
inputProps.onBlur = (e) => onBlurHandler(e, onBlur);
|
||||
inputProps.onFocus = (e) => onClickHandler(e, null);
|
||||
|
||||
if (props.type === 'select') {
|
||||
inputProps.options = props.selectOptions;
|
||||
}
|
||||
if (props.type === 'checkbox') {
|
||||
inputProps.checked = props.isChecked;
|
||||
}
|
||||
|
||||
const validationGroupProps = {
|
||||
for: props.for,
|
||||
};
|
||||
if (!props.optionalFieldCheckbox) {
|
||||
validationGroupProps.invalid = props.invalid;
|
||||
validationGroupProps.invalidMessage = props.invalidMessage;
|
||||
validationGroupProps.helpText = showHelpText ? props.helpText : '';
|
||||
} else {
|
||||
validationGroupProps.className = props.optionalFieldCheckbox ? 'custom-control pt-10 mb-0' : '';
|
||||
}
|
||||
if (props.className) {
|
||||
validationGroupProps.className = props.className;
|
||||
}
|
||||
|
||||
return (
|
||||
<ValidationFormGroup
|
||||
{...validationGroupProps}
|
||||
>
|
||||
{showLabel()}
|
||||
<Input
|
||||
{...inputProps}
|
||||
required
|
||||
/>
|
||||
{showOptional()}
|
||||
</ValidationFormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
AuthnCustomValidationFormGroup.defaultProps = {
|
||||
name: '',
|
||||
for: '',
|
||||
label: '',
|
||||
optionalFieldCheckbox: false,
|
||||
type: '',
|
||||
value: '',
|
||||
invalid: false,
|
||||
invalidMessage: '',
|
||||
helpText: '',
|
||||
className: '',
|
||||
onClick: null,
|
||||
onBlur: null,
|
||||
onChange: null,
|
||||
isChecked: false,
|
||||
checkboxMessage: '',
|
||||
selectOptions: null,
|
||||
};
|
||||
|
||||
AuthnCustomValidationFormGroup.propTypes = {
|
||||
name: PropTypes.string,
|
||||
for: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool,
|
||||
]),
|
||||
invalid: PropTypes.bool,
|
||||
invalidMessage: PropTypes.string,
|
||||
helpText: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
isChecked: PropTypes.bool,
|
||||
optionalFieldCheckbox: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
checkboxMessage: PropTypes.string,
|
||||
selectOptions: PropTypes.arrayOf(PropTypes.shape({
|
||||
key: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
})),
|
||||
};
|
||||
|
||||
export default injectIntl(AuthnCustomValidationFormGroup);
|
||||
@@ -7,6 +7,7 @@ export { default as SocialAuthProviders } from './SocialAuthProviders';
|
||||
export { default as ThirdPartyAuthAlert } from './ThirdPartyAuthAlert';
|
||||
export { default as InstitutionLogistration } from './InstitutionLogistration';
|
||||
export { RenderInstitutionButton } from './InstitutionLogistration';
|
||||
export { default as AuthnValidationFormGroup } from './AuthnValidationFormGroup';
|
||||
export { default as reducer } from './data/reducers';
|
||||
export { default as saga } from './data/sagas';
|
||||
export { storeName } from './data/selectors';
|
||||
|
||||
@@ -11,9 +11,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Alert,
|
||||
Form,
|
||||
Input,
|
||||
StatefulButton,
|
||||
ValidationFormGroup,
|
||||
} from '@edx/paragon';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -21,7 +19,11 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
||||
import { forgotPassword } from './data/actions';
|
||||
import { forgotPasswordResultSelector } from './data/selectors';
|
||||
import RequestInProgressAlert from './RequestInProgressAlert';
|
||||
|
||||
import messages from './messages';
|
||||
import {
|
||||
AuthnValidationFormGroup,
|
||||
} from '../common-components';
|
||||
import APIFailureMessage from '../common-components/APIFailureMessage';
|
||||
import { INTERNAL_SERVER_ERROR, LOGIN_PAGE, VALID_EMAIL_REGEX } from '../data/constants';
|
||||
import LoginHelpLinks from '../login/LoginHelpLinks';
|
||||
@@ -95,26 +97,20 @@ const ForgotPasswordPage = (props) => {
|
||||
<p className="mb-4">
|
||||
{intl.formatMessage(messages['forgot.password.page.instructions'])}
|
||||
</p>
|
||||
<ValidationFormGroup
|
||||
className="mb-0 w-100"
|
||||
for="email"
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['forgot.password.page.email.field.label'])}
|
||||
for="forgot-password-input"
|
||||
name="email"
|
||||
type="email"
|
||||
invalid={validationError !== ''}
|
||||
invalidMessage={validationError}
|
||||
placeholder="username@domain.com"
|
||||
value={values.email}
|
||||
onBlur={() => getValidationMessage(values.email)}
|
||||
onChange={e => setFieldValue('email', e.target.value)}
|
||||
helpText={intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })}
|
||||
>
|
||||
<Form.Label htmlFor="forgot-password-input" className="h6 mr-1">
|
||||
{intl.formatMessage(messages['forgot.password.page.email.field.label'])}
|
||||
</Form.Label>
|
||||
<Input
|
||||
name="email"
|
||||
id="forgot-password-input"
|
||||
type="email"
|
||||
placeholder="username@domain.com"
|
||||
value={values.email}
|
||||
onBlur={() => getValidationMessage(values.email)}
|
||||
onChange={e => setFieldValue('email', e.target.value)}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
className="mb-0 w-100"
|
||||
/>
|
||||
<LoginHelpLinks page="forgot-password" />
|
||||
<StatefulButton
|
||||
type="submit"
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('ForgotPasswordPage', () => {
|
||||
});
|
||||
|
||||
forgotPasswordPage.update();
|
||||
expect(forgotPasswordPage.find('#email-invalid-feedback').text()).toEqual(validationMessage);
|
||||
expect(forgotPasswordPage.find('#forgot-password-input-invalid-feedback').text()).toEqual(validationMessage);
|
||||
});
|
||||
|
||||
it('check cookie rendered', () => {
|
||||
|
||||
@@ -23,29 +23,22 @@ exports[`ForgotPasswordPage should match default section snapshot 1`] = `
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="forgot-password-input"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="forgot-password-input"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<button
|
||||
className="mt-2 field-link small"
|
||||
@@ -144,29 +137,22 @@ exports[`ForgotPasswordPage should match forbidden section snapshot 1`] = `
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="forgot-password-input"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="forgot-password-input"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<button
|
||||
className="mt-2 field-link small"
|
||||
@@ -250,29 +236,22 @@ exports[`ForgotPasswordPage should match pending section snapshot 1`] = `
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="forgot-password-input"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="forgot-password-input"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<button
|
||||
className="mt-2 field-link small"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import {
|
||||
Form, Hyperlink, Input, StatefulButton, ValidationFormGroup,
|
||||
Form, Hyperlink, StatefulButton,
|
||||
} from '@edx/paragon';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
@@ -23,7 +23,7 @@ import LoginFailureMessage from './LoginFailure';
|
||||
import messages from './messages';
|
||||
import {
|
||||
RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton,
|
||||
InstitutionLogistration,
|
||||
InstitutionLogistration, AuthnValidationFormGroup,
|
||||
} from '../common-components';
|
||||
import {
|
||||
DEFAULT_REDIRECT_URL, DEFAULT_STATE, LOGIN_PAGE, REGISTER_PAGE, ENTERPRISE_LOGIN_URL, PENDING_STATE,
|
||||
@@ -209,42 +209,30 @@ class LoginPage extends React.Component {
|
||||
{intl.formatMessage(messages['sign.in.heading'])}
|
||||
</h3>
|
||||
<Form className="m-0">
|
||||
<ValidationFormGroup
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['email.label'])}
|
||||
for="email"
|
||||
name="email"
|
||||
type="email"
|
||||
invalid={this.state.errors.email !== ''}
|
||||
invalidMessage={this.state.email === '' ? intl.formatMessage(messages['email.validation.message']) : intl.formatMessage(messages['email.format.validation.message'])}
|
||||
className="mb-0 w-100"
|
||||
placeholder="username@domain.com"
|
||||
value={this.state.email}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
helpText={intl.formatMessage(messages['email.help.message'])}
|
||||
>
|
||||
<Form.Label htmlFor="loginEmail" className="h6 mr-1">
|
||||
{intl.formatMessage(messages['email.label'])}
|
||||
</Form.Label>
|
||||
<Input
|
||||
name="email"
|
||||
id="loginEmail"
|
||||
type="email"
|
||||
placeholder="username@domain.com"
|
||||
value={this.state.email}
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
className="w-100"
|
||||
/>
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['password.label'])}
|
||||
for="password"
|
||||
name="password"
|
||||
type="password"
|
||||
invalid={this.state.errors.password !== ''}
|
||||
invalidMessage={intl.formatMessage(messages['password.validation.message'])}
|
||||
className="mb-0 w-100"
|
||||
>
|
||||
<Form.Label htmlFor="loginPassword" className="h6 mr-1">
|
||||
{intl.formatMessage(messages['password.label'])}
|
||||
</Form.Label>
|
||||
<Input
|
||||
name="password"
|
||||
id="loginPassword"
|
||||
type="password"
|
||||
value={this.state.password}
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
placeholder=""
|
||||
value={this.state.password}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
/>
|
||||
<LoginHelpLinks page={LOGIN_PAGE} />
|
||||
<Hyperlink className="field-link mt-0 mb-3 small" destination={this.getEnterPriseLoginURL()}>
|
||||
{intl.formatMessage(messages['enterprise.login.link.text'])}
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('LoginPage', () => {
|
||||
const errorState = { email: null, password: '' };
|
||||
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||
|
||||
loginPage.find('input#loginPassword').simulate('change', { target: { value: 'test', name: 'password' } });
|
||||
loginPage.find('input#password').simulate('change', { target: { value: 'test', name: 'password' } });
|
||||
loginPage.find('button.btn-brand').simulate('click');
|
||||
|
||||
expect(loginPage.find('LoginPage').state('errors')).toEqual(errorState);
|
||||
@@ -160,7 +160,7 @@ describe('LoginPage', () => {
|
||||
const errorState = { email: '', password: null };
|
||||
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||
|
||||
loginPage.find('input#loginEmail').simulate('change', { target: { value: 'test@example.com', name: 'email' } });
|
||||
loginPage.find('input#email').simulate('change', { target: { value: 'test@example.com', name: 'email' } });
|
||||
loginPage.find('button.btn-brand').simulate('click');
|
||||
expect(loginPage.find('LoginPage').state('errors')).toEqual(errorState);
|
||||
});
|
||||
@@ -328,7 +328,7 @@ describe('LoginPage', () => {
|
||||
it('form only be scrollable on submission', () => {
|
||||
const loginPage = mount(reduxWrapper(<IntlLoginPage {...props} />));
|
||||
|
||||
loginPage.find('input#loginPassword').simulate('change', { target: { value: 'test@example.com', name: 'password' } });
|
||||
loginPage.find('input#password').simulate('change', { target: { value: 'test@example.com', name: 'password' } });
|
||||
loginPage.find('button.btn-brand').simulate('click');
|
||||
|
||||
expect(loginPage.find(<IntlLoginFailureMessage />)).toBeTruthy();
|
||||
|
||||
@@ -38,30 +38,24 @@ exports[`LoginPage should match TPA provider snapshot 1`] = `
|
||||
className="m-0"
|
||||
>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginEmail"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginEmail"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX.
|
||||
</small>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="email-invalid-feedback"
|
||||
@@ -70,23 +64,24 @@ exports[`LoginPage should match TPA provider snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginPassword"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginPassword"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="password-invalid-feedback"
|
||||
@@ -236,30 +231,24 @@ exports[`LoginPage should match default section snapshot 1`] = `
|
||||
className="m-0"
|
||||
>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginEmail"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginEmail"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX.
|
||||
</small>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="email-invalid-feedback"
|
||||
@@ -268,23 +257,24 @@ exports[`LoginPage should match default section snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginPassword"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginPassword"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="password-invalid-feedback"
|
||||
@@ -436,30 +426,24 @@ exports[`LoginPage should match forget password alert message snapshot 1`] = `
|
||||
className="m-0"
|
||||
>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginEmail"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginEmail"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX.
|
||||
</small>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="email-invalid-feedback"
|
||||
@@ -468,23 +452,24 @@ exports[`LoginPage should match forget password alert message snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginPassword"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginPassword"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="password-invalid-feedback"
|
||||
@@ -598,30 +583,24 @@ exports[`LoginPage should match pending button state snapshot 1`] = `
|
||||
className="m-0"
|
||||
>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginEmail"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginEmail"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX.
|
||||
</small>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="email-invalid-feedback"
|
||||
@@ -630,23 +609,24 @@ exports[`LoginPage should match pending button state snapshot 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginPassword"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginPassword"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="password-invalid-feedback"
|
||||
@@ -797,30 +777,24 @@ exports[`LoginPage should show error message 1`] = `
|
||||
className="m-0"
|
||||
>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginEmail"
|
||||
>
|
||||
Email
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginEmail"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Email"
|
||||
required={true}
|
||||
type="email"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
The email address you used to register with edX.
|
||||
</small>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="email-invalid-feedback"
|
||||
@@ -829,23 +803,24 @@ exports[`LoginPage should show error message 1`] = `
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
className="form-group mb-0 w-100"
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="loginPassword"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="loginPassword"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="password-invalid-feedback"
|
||||
|
||||
@@ -28,7 +28,7 @@ import { registrationRequestSelector } from './data/selectors';
|
||||
import { thirdPartyAuthContextSelector } from '../common-components/data/selectors';
|
||||
import {
|
||||
RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton,
|
||||
InstitutionLogistration,
|
||||
InstitutionLogistration, AuthnValidationFormGroup,
|
||||
} from '../common-components';
|
||||
import RegistrationFailure from './RegistrationFailure';
|
||||
import {
|
||||
@@ -467,26 +467,35 @@ class RegistrationPage extends React.Component {
|
||||
);
|
||||
}
|
||||
if (field.type === 'select') {
|
||||
options = field.options.map((item) => ({
|
||||
value: item.value,
|
||||
label: item.name,
|
||||
}));
|
||||
options = field.options.map((item) => {
|
||||
const option = {};
|
||||
option.value = item.value;
|
||||
option.label = item.name;
|
||||
if (item.name === '--') {
|
||||
option.label = `${field.label} (required)`;
|
||||
option.disabled = true;
|
||||
}
|
||||
return option;
|
||||
});
|
||||
props.options = options;
|
||||
props.onBlur = e => this.handleOnBlur(e);
|
||||
props.onClick = e => this.handleOnClick(e);
|
||||
props.onChange = e => this.handleOnChange(e);
|
||||
}
|
||||
return (
|
||||
<ValidationFormGroup
|
||||
<AuthnValidationFormGroup
|
||||
label={field.label}
|
||||
for={field.name}
|
||||
name={field.name}
|
||||
type={field.type}
|
||||
key={field.name}
|
||||
invalid={this.state.errors[stateVar] !== ''}
|
||||
invalidMessage={field.errorMessages.required}
|
||||
placeholder=""
|
||||
className="mb-0"
|
||||
>
|
||||
<label htmlFor={field.name} className="h6 pt-10">{field.label} (required)</label>
|
||||
<Input {...props} />
|
||||
</ValidationFormGroup>
|
||||
value={props.value}
|
||||
onClick={(e) => this.handleOnClick(e)}
|
||||
onBlur={(e) => this.handleOnBlur(e)}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
selectOptions={props.options}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -651,111 +660,80 @@ class RegistrationPage extends React.Component {
|
||||
<hr className="mb-20 border-gray-200" />
|
||||
<h3 className="mb-20">{intl.formatMessage(messages['create.a.new.account'])}</h3>
|
||||
<Form className="form-group">
|
||||
<ValidationFormGroup
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['fullname.label'])}
|
||||
for="name"
|
||||
name="name"
|
||||
type="text"
|
||||
invalid={this.state.errors.name !== ''}
|
||||
invalidMessage={this.state.errors.name}
|
||||
helpText="This name will be used by any certificates that you earn."
|
||||
>
|
||||
<label htmlFor="name" className="h6 pt-10">
|
||||
{intl.formatMessage(messages['fullname.label'])}
|
||||
</label>
|
||||
<Input
|
||||
name="name"
|
||||
id="name"
|
||||
type="text"
|
||||
placeholder=""
|
||||
value={this.state.name}
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
onBlur={e => this.handleOnBlur(e)}
|
||||
onClick={e => this.handleOnClick(e)}
|
||||
required
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
placeholder=""
|
||||
value={this.state.name}
|
||||
onClick={(e) => this.handleOnClick(e)}
|
||||
onBlur={(e) => this.handleOnBlur(e)}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
helpText={intl.formatMessage(messages['helptext.name'])}
|
||||
/>
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['username.label'])}
|
||||
for="username"
|
||||
name="username"
|
||||
type="text"
|
||||
invalid={this.state.errors.username !== ''}
|
||||
invalidMessage={this.state.errors.username}
|
||||
helpText="The name that will identify you in your courses. It cannot be changed later."
|
||||
>
|
||||
<label htmlFor="username" className="h6 pt-10">
|
||||
{intl.formatMessage(messages['username.label'])}
|
||||
</label>
|
||||
<Input
|
||||
name="username"
|
||||
id="username"
|
||||
type="text"
|
||||
placeholder=""
|
||||
value={this.state.username}
|
||||
maxLength="30"
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
onBlur={e => this.handleOnBlur(e)}
|
||||
onClick={e => this.handleOnClick(e)}
|
||||
required
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
placeholder=""
|
||||
value={this.state.username}
|
||||
onClick={(e) => this.handleOnClick(e)}
|
||||
onBlur={(e) => this.handleOnBlur(e)}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
helpText={intl.formatMessage(messages['helptext.username'])}
|
||||
/>
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['register.page.email.label'])}
|
||||
for="email"
|
||||
name="email"
|
||||
type="text"
|
||||
invalid={this.state.errors.email !== ''}
|
||||
invalidMessage={this.state.errors.email}
|
||||
helpText="This is what you will use to login."
|
||||
>
|
||||
<label htmlFor="email" className="h6 pt-10">
|
||||
{intl.formatMessage(messages['register.page.email.label'])}
|
||||
</label>
|
||||
<Input
|
||||
name="email"
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="username@domain.com"
|
||||
value={this.state.email}
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
onBlur={e => this.handleOnBlur(e)}
|
||||
onClick={e => this.handleOnClick(e)}
|
||||
required
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
placeholder=""
|
||||
value={this.state.email}
|
||||
onClick={(e) => this.handleOnClick(e)}
|
||||
onBlur={(e) => this.handleOnBlur(e)}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
helpText={intl.formatMessage(messages['helptext.email'])}
|
||||
/>
|
||||
{!currentProvider && (
|
||||
<ValidationFormGroup
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['password.label'])}
|
||||
for="password"
|
||||
name="password"
|
||||
type="password"
|
||||
invalid={this.state.errors.password !== ''}
|
||||
invalidMessage={this.state.errors.password}
|
||||
helpText="Your password must contain at least 8 characters, including 1 letter & 1 number."
|
||||
>
|
||||
<label htmlFor="password" className="h6 pt-10">
|
||||
{intl.formatMessage(messages['password.label'])}
|
||||
</label>
|
||||
<Input
|
||||
name="password"
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder=""
|
||||
value={this.state.password}
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
onBlur={e => this.handleOnBlur(e)}
|
||||
onClick={e => this.handleOnClick(e)}
|
||||
required
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
placeholder=""
|
||||
value={this.state.password}
|
||||
onClick={(e) => this.handleOnClick(e)}
|
||||
onBlur={(e) => this.handleOnBlur(e)}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
helpText={intl.formatMessage(messages['helptext.password'])}
|
||||
/>
|
||||
)}
|
||||
{ this.addExtraRequiredFields() }
|
||||
<ValidationFormGroup
|
||||
<AuthnValidationFormGroup
|
||||
label=""
|
||||
for="optional"
|
||||
className="custom-control pt-10 mb-0"
|
||||
>
|
||||
<Input
|
||||
name="optional"
|
||||
id="optional"
|
||||
type="checkbox"
|
||||
value={this.state.enableOptionalField}
|
||||
checked={this.state.enableOptionalField}
|
||||
onChange={e => this.handleOnOptional(e)}
|
||||
required
|
||||
/>
|
||||
<p role="presentation" id="additionalFields" className="mb-0 small" onClick={e => this.handleOnOptional(e)}>
|
||||
{intl.formatMessage(messages['support.education.research'])}
|
||||
</p>
|
||||
</ValidationFormGroup>
|
||||
name="optional"
|
||||
type="checkbox"
|
||||
invalidMessage=""
|
||||
placeholder=""
|
||||
value={this.state.enableOptionalField}
|
||||
onClick={(e) => this.handleOnOptional(e)}
|
||||
onBlur={null}
|
||||
onChange={(e) => this.handleOnOptional(e)}
|
||||
optionalFieldCheckbox
|
||||
isChecked={this.state.enableOptionalField}
|
||||
checkboxMessage={intl.formatMessage(messages['support.education.research'])}
|
||||
/>
|
||||
{ this.state.enableOptionalField ? this.addExtraOptionalFields() : null}
|
||||
<StatefulButton
|
||||
type="button"
|
||||
|
||||
@@ -141,6 +141,26 @@ const messages = defineMessages({
|
||||
defaultMessage: 'We couldn\'t create your account.',
|
||||
description: 'error message when registration failure.',
|
||||
},
|
||||
'helptext.name': {
|
||||
id: 'helptext.name',
|
||||
defaultMessage: 'This name will be used by any certificates that you earn.',
|
||||
description: '',
|
||||
},
|
||||
'helptext.username': {
|
||||
id: 'helptext.username',
|
||||
defaultMessage: 'The name that will identify you in your courses. It cannot be changed later.',
|
||||
description: '',
|
||||
},
|
||||
'helptext.password': {
|
||||
id: 'helptext.password',
|
||||
defaultMessage: 'Your password must contain at least 8 characters, including 1 letter & 1 number.',
|
||||
description: '',
|
||||
},
|
||||
'helptext.email': {
|
||||
id: 'helptext.email',
|
||||
defaultMessage: 'This is what you will use to login.',
|
||||
description: '',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -48,90 +48,62 @@ exports[`./RegistrationPage.js should display no password field when current pro
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="name"
|
||||
>
|
||||
Full Name (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="name-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Full Name (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="name-help-text"
|
||||
>
|
||||
This name will be used by any certificates that you earn.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="username"
|
||||
>
|
||||
Public Username (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="username-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="username"
|
||||
maxLength="30"
|
||||
name="username"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Public Username (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="username-help-text"
|
||||
>
|
||||
The name that will identify you in your courses. It cannot be changed later.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="email"
|
||||
>
|
||||
Email (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onFocus={[Function]}
|
||||
placeholder="Email (required)"
|
||||
required={true}
|
||||
type="email"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
This is what you will use to login.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group custom-control small"
|
||||
@@ -166,13 +138,18 @@ exports[`./RegistrationPage.js should display no password field when current pro
|
||||
<div
|
||||
className="form-group custom-control pt-10 mb-0"
|
||||
>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
checked={false}
|
||||
className="form-check-input"
|
||||
id="optional"
|
||||
name="optional"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder=""
|
||||
required={true}
|
||||
type="checkbox"
|
||||
value={false}
|
||||
@@ -245,119 +222,82 @@ exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = `
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="name"
|
||||
>
|
||||
Full Name (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="name-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Full Name (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="name-help-text"
|
||||
>
|
||||
This name will be used by any certificates that you earn.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="username"
|
||||
>
|
||||
Public Username (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="username-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="username"
|
||||
maxLength="30"
|
||||
name="username"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Public Username (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="username-help-text"
|
||||
>
|
||||
The name that will identify you in your courses. It cannot be changed later.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="email"
|
||||
>
|
||||
Email (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onFocus={[Function]}
|
||||
placeholder="Email (required)"
|
||||
required={true}
|
||||
type="email"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
This is what you will use to login.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="password"
|
||||
>
|
||||
Password (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="password-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Password (required)"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="password-help-text"
|
||||
>
|
||||
Your password must contain at least 8 characters, including 1 letter & 1 number.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group custom-control small"
|
||||
@@ -392,13 +332,18 @@ exports[`./RegistrationPage.js should match TPA provider snapshot 1`] = `
|
||||
<div
|
||||
className="form-group custom-control pt-10 mb-0"
|
||||
>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
checked={false}
|
||||
className="form-check-input"
|
||||
id="optional"
|
||||
name="optional"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder=""
|
||||
required={true}
|
||||
type="checkbox"
|
||||
value={false}
|
||||
@@ -510,119 +455,82 @@ exports[`./RegistrationPage.js should match default section snapshot 1`] = `
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="name"
|
||||
>
|
||||
Full Name (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="name-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Full Name (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="name-help-text"
|
||||
>
|
||||
This name will be used by any certificates that you earn.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="username"
|
||||
>
|
||||
Public Username (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="username-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="username"
|
||||
maxLength="30"
|
||||
name="username"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Public Username (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="username-help-text"
|
||||
>
|
||||
The name that will identify you in your courses. It cannot be changed later.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="email"
|
||||
>
|
||||
Email (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onFocus={[Function]}
|
||||
placeholder="Email (required)"
|
||||
required={true}
|
||||
type="email"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
This is what you will use to login.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="password"
|
||||
>
|
||||
Password (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="password-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Password (required)"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="password-help-text"
|
||||
>
|
||||
Your password must contain at least 8 characters, including 1 letter & 1 number.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group custom-control small"
|
||||
@@ -657,13 +565,18 @@ exports[`./RegistrationPage.js should match default section snapshot 1`] = `
|
||||
<div
|
||||
className="form-group custom-control pt-10 mb-0"
|
||||
>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
checked={false}
|
||||
className="form-check-input"
|
||||
id="optional"
|
||||
name="optional"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder=""
|
||||
required={true}
|
||||
type="checkbox"
|
||||
value={false}
|
||||
@@ -736,119 +649,82 @@ exports[`./RegistrationPage.js should match pending button state snapshot 1`] =
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="name"
|
||||
>
|
||||
Full Name (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="name-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Full Name (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="name-help-text"
|
||||
>
|
||||
This name will be used by any certificates that you earn.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="username"
|
||||
>
|
||||
Public Username (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="username-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="username"
|
||||
maxLength="30"
|
||||
name="username"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Public Username (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="username-help-text"
|
||||
>
|
||||
The name that will identify you in your courses. It cannot be changed later.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="email"
|
||||
>
|
||||
Email (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onFocus={[Function]}
|
||||
placeholder="Email (required)"
|
||||
required={true}
|
||||
type="email"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
This is what you will use to login.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="password"
|
||||
>
|
||||
Password (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="password-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Password (required)"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="password-help-text"
|
||||
>
|
||||
Your password must contain at least 8 characters, including 1 letter & 1 number.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group custom-control small"
|
||||
@@ -883,13 +759,18 @@ exports[`./RegistrationPage.js should match pending button state snapshot 1`] =
|
||||
<div
|
||||
className="form-group custom-control pt-10 mb-0"
|
||||
>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
checked={false}
|
||||
className="form-check-input"
|
||||
id="optional"
|
||||
name="optional"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder=""
|
||||
required={true}
|
||||
type="checkbox"
|
||||
value={false}
|
||||
@@ -1005,119 +886,82 @@ exports[`./RegistrationPage.js should show error message on 409 on alert and bel
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="name"
|
||||
>
|
||||
Full Name (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="name-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Full Name (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="name-help-text"
|
||||
>
|
||||
This name will be used by any certificates that you earn.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="username"
|
||||
>
|
||||
Public Username (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="username-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="username"
|
||||
maxLength="30"
|
||||
name="username"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Public Username (required)"
|
||||
required={true}
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="username-help-text"
|
||||
>
|
||||
The name that will identify you in your courses. It cannot be changed later.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="email"
|
||||
>
|
||||
Email (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="email-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder="username@domain.com"
|
||||
onFocus={[Function]}
|
||||
placeholder="Email (required)"
|
||||
required={true}
|
||||
type="email"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="email-help-text"
|
||||
>
|
||||
This is what you will use to login.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
className="h6 pt-10"
|
||||
htmlFor="password"
|
||||
>
|
||||
Password (required)
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby="password-help-text"
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
placeholder=""
|
||||
onFocus={[Function]}
|
||||
placeholder="Password (required)"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<small
|
||||
className="form-text text-muted"
|
||||
id="password-help-text"
|
||||
>
|
||||
Your password must contain at least 8 characters, including 1 letter & 1 number.
|
||||
</small>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group custom-control small"
|
||||
@@ -1152,13 +996,18 @@ exports[`./RegistrationPage.js should show error message on 409 on alert and bel
|
||||
<div
|
||||
className="form-group custom-control pt-10 mb-0"
|
||||
>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
checked={false}
|
||||
className="form-check-input"
|
||||
id="optional"
|
||||
name="optional"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder=""
|
||||
required={true}
|
||||
type="checkbox"
|
||||
value={false}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
Alert, Form, Input, StatefulButton, ValidationFormGroup,
|
||||
Alert, Form, StatefulButton,
|
||||
} from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getQueryParameters } from '@edx/frontend-platform';
|
||||
@@ -15,6 +15,9 @@ import { resetPasswordResultSelector } from './data/selectors';
|
||||
import { validatePassword } from './data/service';
|
||||
import InvalidTokenMessage from './InvalidToken';
|
||||
import ResetSuccessMessage from './ResetSuccess';
|
||||
import {
|
||||
AuthnValidationFormGroup,
|
||||
} from '../common-components';
|
||||
import Spinner from './Spinner';
|
||||
|
||||
const ResetPasswordPage = (props) => {
|
||||
@@ -50,6 +53,11 @@ const ResetPasswordPage = (props) => {
|
||||
const handleNewPasswordChange = (e) => {
|
||||
const newPassword = e.target.value;
|
||||
setNewPasswordValue(newPassword);
|
||||
};
|
||||
|
||||
const handleNewPasswordOnBlur = (e) => {
|
||||
const newPassword = e.target.value;
|
||||
setNewPasswordValue(newPassword);
|
||||
|
||||
if (newPassword === '') {
|
||||
setPasswordValidValue(false);
|
||||
@@ -115,41 +123,31 @@ const ResetPasswordPage = (props) => {
|
||||
<p className="mb-4">
|
||||
{intl.formatMessage(messages['reset.password.page.instructions'])}
|
||||
</p>
|
||||
<ValidationFormGroup
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['reset.password.page.new.field.label'])}
|
||||
for="reset-password-input"
|
||||
name="new-password1"
|
||||
type="password"
|
||||
invalid={!passwordValid}
|
||||
invalidMessage={validationMessage}
|
||||
placeholder=""
|
||||
value={newPasswordInput}
|
||||
onChange={e => handleNewPasswordChange(e)}
|
||||
onBlur={e => handleNewPasswordOnBlur(e)}
|
||||
className="w-100"
|
||||
>
|
||||
<Form.Label htmlFor="reset-password-input" className="h6 mr-1">
|
||||
{intl.formatMessage(messages['reset.password.page.new.field.label'])}
|
||||
</Form.Label>
|
||||
<Input
|
||||
name="new-password1"
|
||||
id="reset-password-input"
|
||||
type="password"
|
||||
placeholder=""
|
||||
onBlur={e => handleNewPasswordChange(e)}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
<ValidationFormGroup
|
||||
/>
|
||||
<AuthnValidationFormGroup
|
||||
label={intl.formatMessage(messages['reset.password.page.confirm.field.label'])}
|
||||
for="confirm-password-input"
|
||||
name="new-password2"
|
||||
type="password"
|
||||
invalid={!passwordMatch}
|
||||
invalidMessage={intl.formatMessage(messages['reset.password.page.invalid.match.message'])}
|
||||
placeholder=""
|
||||
value={confirmPasswordInput}
|
||||
onChange={e => handleConfirmPasswordChange(e)}
|
||||
className="w-100"
|
||||
>
|
||||
<Form.Label htmlFor="confirm-password-input" className="h6 mr-1">
|
||||
{intl.formatMessage(messages['reset.password.page.confirm.field.label'])}
|
||||
</Form.Label>
|
||||
<Input
|
||||
name="new-password2"
|
||||
id="confirm-password-input"
|
||||
type="password"
|
||||
placeholder=""
|
||||
value={confirmPasswordInput}
|
||||
onChange={e => handleConfirmPasswordChange(e)}
|
||||
/>
|
||||
</ValidationFormGroup>
|
||||
/>
|
||||
<StatefulButton
|
||||
type="submit"
|
||||
className="btn-primary"
|
||||
|
||||
@@ -63,41 +63,42 @@ exports[`ResetPasswordPage should match pending reset message section snapshot 1
|
||||
<div
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="reset-password-input"
|
||||
>
|
||||
New Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="reset-password-input"
|
||||
name="new-password1"
|
||||
onBlur={[Function]}
|
||||
placeholder=""
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="New Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="confirm-password-input"
|
||||
>
|
||||
Confirm Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="confirm-password-input"
|
||||
name="new-password2"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder=""
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Confirm Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="confirm-password-input-invalid-feedback"
|
||||
@@ -169,41 +170,42 @@ exports[`ResetPasswordPage should match reset password default section snapshot
|
||||
<div
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="reset-password-input"
|
||||
>
|
||||
New Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="reset-password-input"
|
||||
name="new-password1"
|
||||
onBlur={[Function]}
|
||||
placeholder=""
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="New Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
</div>
|
||||
<div
|
||||
className="form-group w-100"
|
||||
>
|
||||
<label
|
||||
className="h6 mr-1 form-label"
|
||||
htmlFor="confirm-password-input"
|
||||
>
|
||||
Confirm Password
|
||||
</label>
|
||||
<span />
|
||||
<input
|
||||
aria-describedby=""
|
||||
className="form-control"
|
||||
id="confirm-password-input"
|
||||
name="new-password2"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
placeholder=""
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
placeholder="Confirm Password"
|
||||
required={true}
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<span />
|
||||
<strong
|
||||
className="invalid-feedback"
|
||||
id="confirm-password-input-invalid-feedback"
|
||||
|
||||
Reference in New Issue
Block a user