Skip to content

Commit

Permalink
Refactor login page to use functional component (#899)
Browse files Browse the repository at this point in the history
* refactor: zamir/van 1390/add basic login form (#894)
Description: login refactor

* feat: add basic login form
* feat: remove cookie logic from login page

* refactor: refactor social auth, tpahint and institution login (#895)

* feat: add basic login form

* feat: add error handling

* refactor: refactor social auth, tpahint and institution login

Description:

Refactor the following flows:
1 - Institution login
2 - SSO login
3 - Tpahint

VAN-1391

---------

Co-authored-by: Zainab Amir <[email protected]>

* fix: tests and lint issues (#905)

* feat: add tests

* fix: rebase this branch with master
Description:
Rebase with master branch
VAN-1413

* chore: update variable name

* fix: fix When using tpa-hint don't show the login form
Description: When using tpa-hint don't show the login form
VAN-1801

---------

Co-authored-by: Blue <[email protected]>
Co-authored-by: ahtesham-quraish <[email protected]>
  • Loading branch information
3 people authored Jan 24, 2024
1 parent 2ea9301 commit ad7099a
Show file tree
Hide file tree
Showing 23 changed files with 848 additions and 813 deletions.
4 changes: 3 additions & 1 deletion src/common-components/PasswordField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const PasswordField = (props) => {
{props.errorMessage !== '' && (
<Form.Control.Feedback key="error" className="form-text-size" hasIcon={false} feedback-for={props.name} type="invalid">
{props.errorMessage}
<span className="sr-only">{formatMessage(messages['password.sr.only.helping.text'])}</span>
{props.showScreenReaderText && <span className="sr-only">{formatMessage(messages['password.sr.only.helping.text'])}</span>}
</Form.Control.Feedback>
)}
</Form.Group>
Expand All @@ -153,6 +153,7 @@ PasswordField.defaultProps = {
handleChange: () => {},
handleErrorChange: null,
showRequirements: true,
showScreenReaderText: true,
autoComplete: null,
};

Expand All @@ -168,6 +169,7 @@ PasswordField.propTypes = {
showRequirements: PropTypes.bool,
value: PropTypes.string.isRequired,
autoComplete: PropTypes.string,
showScreenReaderText: PropTypes.bool,
};

export default PasswordField;
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,56 @@ import React from 'react';

import { getConfig } from '@edx/frontend-platform';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Hyperlink, Icon,
} from '@edx/paragon';
import { Institution } from '@edx/paragon/icons';
import PropTypes from 'prop-types';
import Skeleton from 'react-loading-skeleton';

import messages from './messages';
import {
ENTERPRISE_LOGIN_URL, LOGIN_PAGE, PENDING_STATE, REGISTER_PAGE,
} from '../data/constants';

import {
RenderInstitutionButton,
SocialAuthProviders,
} from '../../common-components';
import {
PENDING_STATE, REGISTER_PAGE,
} from '../../data/constants';
import messages from '../messages';
} from './index';

/**
* This component renders the Single sign-on (SSO) buttons for the providers passed.
* */
const ThirdPartyAuth = (props) => {
const { formatMessage } = useIntl();
const {
providers, secondaryProviders, currentProvider, handleInstitutionLogin, thirdPartyAuthApiStatus,
providers,
secondaryProviders,
currentProvider,
handleInstitutionLogin,
thirdPartyAuthApiStatus,
isLoginPage,
} = props;
const isInstitutionAuthActive = !!secondaryProviders.length && !currentProvider;
const isSocialAuthActive = !!providers.length && !currentProvider;
const isEnterpriseLoginDisabled = getConfig().DISABLE_ENTERPRISE_LOGIN;
const enterpriseLoginURL = getConfig().LMS_BASE_URL + ENTERPRISE_LOGIN_URL;

return (
<>
{((isEnterpriseLoginDisabled && isInstitutionAuthActive) || isSocialAuthActive) && (
<div className="mt-4 mb-3 h4">
{formatMessage(messages['registration.other.options.heading'])}
{isLoginPage
? formatMessage(messages['login.other.options.heading'])
: formatMessage(messages['registration.other.options.heading'])}
</div>
)}
{(isLoginPage && !isEnterpriseLoginDisabled && isSocialAuthActive) && (
<Hyperlink className="btn btn-link btn-sm text-body p-0 mb-4" destination={enterpriseLoginURL}>
<Icon src={Institution} className="institute-icon" />
{formatMessage(messages['enterprise.login.btn.text'])}
</Hyperlink>
)}

{thirdPartyAuthApiStatus === PENDING_STATE ? (
<Skeleton className="tpa-skeleton" height={36} count={2} />
Expand All @@ -41,12 +60,15 @@ const ThirdPartyAuth = (props) => {
{(isEnterpriseLoginDisabled && isInstitutionAuthActive) && (
<RenderInstitutionButton
onSubmitHandler={handleInstitutionLogin}
buttonTitle={formatMessage(messages['register.institution.login.button'])}
buttonTitle={formatMessage(messages['institution.login.button'])}
/>
)}
{isSocialAuthActive && (
<div className="row m-0">
<SocialAuthProviders socialAuthProviders={providers} referrer={REGISTER_PAGE} />
<SocialAuthProviders
socialAuthProviders={providers}
referrer={isLoginPage ? LOGIN_PAGE : REGISTER_PAGE}
/>
</div>
)}
</>
Expand All @@ -59,7 +81,8 @@ ThirdPartyAuth.defaultProps = {
currentProvider: null,
providers: [],
secondaryProviders: [],
thirdPartyAuthApiStatus: 'pending',
thirdPartyAuthApiStatus: PENDING_STATE,
isLoginPage: false,
};

ThirdPartyAuth.propTypes = {
Expand All @@ -86,6 +109,7 @@ ThirdPartyAuth.propTypes = {
}),
),
thirdPartyAuthApiStatus: PropTypes.string,
isLoginPage: PropTypes.bool,
};

export default ThirdPartyAuth;
20 changes: 20 additions & 0 deletions src/common-components/messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ const messages = defineMessages({
description: 'Select ticket form',
defaultMessage: 'Please choose your request type:',
},
'registration.other.options.heading': {
id: 'registration.other.options.heading',
defaultMessage: 'Or register with:',
description: 'A message that appears above third party auth providers i.e saml, google, facebook etc',
},
'institution.login.button': {
id: 'institution.login.button',
defaultMessage: 'Institution/campus credentials',
description: 'shows institutions list',
},
'login.other.options.heading': {
id: 'login.other.options.heading',
defaultMessage: 'Or sign in with:',
description: 'Text that appears above other sign in options like social auth buttons',
},
'enterprise.login.btn.text': {
id: 'enterprise.login.btn.text',
defaultMessage: 'Company or school credentials',
description: 'Company or school login link text.',
},
});

export default messages;
31 changes: 18 additions & 13 deletions src/login/AccountActivationMessage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,30 @@ import PropTypes from 'prop-types';
import { ACCOUNT_ACTIVATION_MESSAGE } from './data/constants';
import messages from './messages';

const AccountActivationMessage = (props) => {
const AccountActivationMessage = ({ messageType }) => {
const { formatMessage } = useIntl();
const { messageType } = props;
const variant = messageType === ACCOUNT_ACTIVATION_MESSAGE.ERROR ? 'danger' : messageType;

const activationOrVerification = getConfig().MARKETING_EMAILS_OPT_IN ? 'confirmation' : 'activation';

let activationMessage;
let heading;
if (!messageType) {
return null;
}

const variant = messageType === ACCOUNT_ACTIVATION_MESSAGE.ERROR ? 'danger' : messageType;
const activationOrConfirmation = getConfig().MARKETING_EMAILS_OPT_IN ? 'confirmation' : 'activation';
const iconMapping = {
[ACCOUNT_ACTIVATION_MESSAGE.SUCCESS]: CheckCircle,
[ACCOUNT_ACTIVATION_MESSAGE.ERROR]: Error,
};

let activationMessage;
let heading;
switch (messageType) {
case ACCOUNT_ACTIVATION_MESSAGE.SUCCESS: {
heading = formatMessage(messages[`account.${activationOrVerification}.success.message.title`]);
activationMessage = <span>{formatMessage(messages[`account.${activationOrVerification}.success.message`])}</span>;
heading = formatMessage(messages[`account.${activationOrConfirmation}.success.message.title`]);
activationMessage = <span>{formatMessage(messages[`account.${activationOrConfirmation}.success.message`])}</span>;
break;
}
case ACCOUNT_ACTIVATION_MESSAGE.INFO: {
activationMessage = formatMessage(messages[`account.${activationOrVerification}.info.message`]);
activationMessage = formatMessage(messages[`account.${activationOrConfirmation}.info.message`]);
break;
}
case ACCOUNT_ACTIVATION_MESSAGE.ERROR: {
Expand All @@ -41,7 +42,7 @@ const AccountActivationMessage = (props) => {
</Alert.Link>
);

heading = formatMessage(messages[`account.${activationOrVerification}.error.message.title`]);
heading = formatMessage(messages[`account.${activationOrConfirmation}.error.message.title`]);
activationMessage = (
<FormattedMessage
id="account.activation.error.message"
Expand All @@ -59,7 +60,7 @@ const AccountActivationMessage = (props) => {
return activationMessage ? (
<Alert
id="account-activation-message"
className="mb-4"
className="mb-5"
variant={variant}
icon={iconMapping[messageType]}
>
Expand All @@ -70,7 +71,11 @@ const AccountActivationMessage = (props) => {
};

AccountActivationMessage.propTypes = {
messageType: PropTypes.string.isRequired,
messageType: PropTypes.string,
};

AccountActivationMessage.defaultProps = {
messageType: null,
};

export default AccountActivationMessage;
Loading

0 comments on commit ad7099a

Please sign in to comment.