From 2b5bb2d44bb3a5d52a9a51a205580b4c351d6e59 Mon Sep 17 00:00:00 2001 From: Adeel Khan Date: Tue, 2 Feb 2021 19:26:36 +0500 Subject: [PATCH] Adds a custom component to handle focusin/out for helptext and label VAN-325 --- .../AuthnValidationFormGroup.jsx | 148 +++++++ src/common-components/index.jsx | 1 + src/forgot-password/ForgotPasswordPage.jsx | 34 +- .../tests/ForgotPasswordPage.test.jsx | 2 +- .../ForgotPasswordPage.test.jsx.snap | 63 +-- src/login/LoginPage.jsx | 50 +-- src/login/tests/LoginPage.test.jsx | 6 +- .../__snapshots__/LoginPage.test.jsx.snap | 215 +++++----- src/register/RegistrationPage.jsx | 182 ++++---- src/register/messages.jsx | 20 + .../RegistrationPage.test.jsx.snap | 401 ++++++------------ src/reset-password/ResetPasswordPage.jsx | 54 ++- .../ResetPasswordPage.test.jsx.snap | 58 +-- 13 files changed, 584 insertions(+), 650 deletions(-) create mode 100644 src/common-components/AuthnValidationFormGroup.jsx diff --git a/src/common-components/AuthnValidationFormGroup.jsx b/src/common-components/AuthnValidationFormGroup.jsx new file mode 100644 index 00000000..909db75b --- /dev/null +++ b/src/common-components/AuthnValidationFormGroup.jsx @@ -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) ? ( + + {props.label} + + ) : ; + return fieldLabel; + }; + const showOptional = () => { + const additionalField = props.optionalFieldCheckbox ? ( + + ) : ; + 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 ( + + {showLabel()} + + {showOptional()} + + ); +}; + +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); diff --git a/src/common-components/index.jsx b/src/common-components/index.jsx index 43e9348a..69194787 100644 --- a/src/common-components/index.jsx +++ b/src/common-components/index.jsx @@ -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'; diff --git a/src/forgot-password/ForgotPasswordPage.jsx b/src/forgot-password/ForgotPasswordPage.jsx index 39226085..1861e29a 100644 --- a/src/forgot-password/ForgotPasswordPage.jsx +++ b/src/forgot-password/ForgotPasswordPage.jsx @@ -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) => {

{intl.formatMessage(messages['forgot.password.page.instructions'])}

- getValidationMessage(values.email)} + onChange={e => setFieldValue('email', e.target.value)} helpText={intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })} - > - - {intl.formatMessage(messages['forgot.password.page.email.field.label'])} - - getValidationMessage(values.email)} - onChange={e => setFieldValue('email', e.target.value)} - /> - + className="mb-0 w-100" + /> { }); 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', () => { diff --git a/src/forgot-password/tests/__snapshots__/ForgotPasswordPage.test.jsx.snap b/src/forgot-password/tests/__snapshots__/ForgotPasswordPage.test.jsx.snap index 838bb3ea..2c8e05bc 100644 --- a/src/forgot-password/tests/__snapshots__/ForgotPasswordPage.test.jsx.snap +++ b/src/forgot-password/tests/__snapshots__/ForgotPasswordPage.test.jsx.snap @@ -23,29 +23,22 @@ exports[`ForgotPasswordPage should match default section snapshot 1`] = `
- + - - The email address you used to register with edX - +