@brightlayer-ui/react-auth-workflow
Version:
Re-usable workflow components for Authentication and Registration within Eaton applications.
216 lines (215 loc) • 15.6 kB
JavaScript
/* eslint-disable @typescript-eslint/no-unused-expressions */
import React, { useCallback, useRef, useState } from 'react';
import { WorkflowCard } from '../../components/WorkflowCard/index.js';
import { WorkflowCardBody } from '../../components/WorkflowCard/WorkflowCardBody.js';
import TextField from '@mui/material/TextField';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import cyberSecurityBadge from '../../assets/images/cybersecurity_certified.png';
import { PasswordTextField } from '../../components/index.js';
import Button from '@mui/material/Button';
import { useTheme } from '@mui/material/styles';
import Checkbox from '@mui/material/Checkbox';
import { HELPER_TEXT_HEIGHT } from '../../utils/constants.js';
import { getLoginScreenUtilityClass } from './utilityClasses.js';
import { unstable_composeClasses as composeClasses } from '@mui/material';
import ErrorManager from '../../components/Error/ErrorManager.js';
import { LinkStyles } from '../../styles/index.js';
const useUtilityClasses = (ownerState) => {
const { classes } = ownerState;
const slots = {
root: ['root'],
projectImageWrapper: ['projectImageWrapper'],
inputFieldsWrapper: ['inputFieldsWrapper'],
usernameTextField: ['usernameTextField'],
passwordTextField: ['passwordTextField'],
rememberMeLoginRowWrapper: ['rememberMeLoginRowWrapper'],
rememberMeWrapper: ['rememberMeWrapper'],
rememberMeCheckbox: ['rememberMeCheckbox'],
rememberMeLabel: ['rememberMeLabel'],
loginButtonWrapper: ['loginButtonWrapper'],
loginButton: ['loginButton'],
forgotPasswordWrapper: ['forgotPasswordWrapper'],
forgotPasswordLabel: ['forgotPasswordLabel'],
selfRegisterWrapper: ['selfRegisterWrapper'],
selfRegisterInstructionLabel: ['selfRegisterInstructionLabel'],
selfRegisterLabel: ['selfRegisterLabel'],
contactSupportWrapper: ['contactSupportWrapper'],
contactSupportLabel: ['contactSupportLabel'],
cyberSecurityBadgeWrapper: ['cyberSecurityBadgeWrapper'],
cyberSecurityBadge: ['cyberSecurityBadge'],
};
return composeClasses(slots, getLoginScreenUtilityClass, classes);
};
/**
* Component that renders a login screen that prompts a user to enter a username and password to login.
*
* @param {LoginScreenProps} props - props of LoginScreen base component
*
* @category Component
*/
export const LoginScreenBase = (props) => {
const { usernameLabel, usernameTextFieldProps, usernameValidator, initialUsernameValue, passwordLabel, passwordTextFieldProps, passwordValidator, showRememberMe, rememberMeLabel, rememberMeInitialValue, onRememberMeChanged, loginButtonLabel, onLogin, showForgotPassword, forgotPasswordLabel, onForgotPassword, showSelfRegistration, selfRegisterButtonLabel, selfRegisterInstructions, onSelfRegister, showContactSupport, contactSupportLabel, onContactSupport, errorDisplayConfig, showCyberSecurityBadge, projectImage, header, footer, ...otherProps } = props;
const theme = useTheme();
const defaultClasses = useUtilityClasses(props);
const [username, setUsername] = React.useState(initialUsernameValue || '');
const [password, setPassword] = React.useState('');
const [rememberMe, setRememberMe] = React.useState(rememberMeInitialValue);
const [shouldValidateUsername, setShouldValidateUsername] = React.useState(false);
const [shouldValidatePassword, setShouldValidatePassword] = React.useState(false);
const passwordField = useRef(null);
const [isUsernameValid, setIsUsernameValid] = useState(usernameValidator ? usernameValidator(username) : true);
const [isPasswordValid, setIsPasswordValid] = useState(passwordValidator ? passwordValidator(password) : true);
const [usernameError, setUsernameError] = useState(isUsernameValid === true ? '' : isUsernameValid);
const [passwordError, setPasswordError] = useState(isPasswordValid === true ? '' : isPasswordValid);
const handleUsernameInputChange = useCallback((value) => {
setUsername(value);
const validatorResponse = usernameValidator?.(value);
setIsUsernameValid(typeof validatorResponse === 'boolean' ? validatorResponse : false);
setUsernameError(typeof validatorResponse === 'string' ? validatorResponse : '');
}, [usernameValidator]);
const handlePasswordInputChange = useCallback((value) => {
setPassword(value);
const validatorResponse = passwordValidator?.(value);
setIsPasswordValid(typeof validatorResponse === 'boolean' ? validatorResponse : false);
setPasswordError(typeof validatorResponse === 'string' ? validatorResponse : '');
}, [passwordValidator]);
const handleLogin = () => {
if (onLogin)
void onLogin(username, password, rememberMe);
};
const handleForgotPassword = () => {
if (onForgotPassword)
onForgotPassword();
};
const handleSelfRegister = () => {
if (onSelfRegister)
onSelfRegister();
};
const handleContactSupport = () => {
if (onContactSupport)
onContactSupport();
};
const handleRememberMeChanged = (value) => {
if (onRememberMeChanged) {
onRememberMeChanged(value);
setRememberMe(value);
}
};
const isFormValid = () => typeof isUsernameValid === 'boolean' &&
isUsernameValid &&
typeof isPasswordValid === 'boolean' &&
isPasswordValid;
const handleLoginSubmit = (e) => {
if (e.key === 'Enter' && isFormValid()) {
void handleLogin();
}
};
return (React.createElement(WorkflowCard, { className: defaultClasses.root, "data-testid": defaultClasses.root, ...otherProps },
React.createElement(WorkflowCardBody, { sx: { py: { xs: 4, sm: 4, md: 4 }, px: { xs: 4, sm: 8, md: 8 } } },
header,
React.createElement(Box, { sx: { display: 'flex', maxWidth: '100%', mb: 6 }, className: defaultClasses.projectImageWrapper, "data-testid": defaultClasses.projectImageWrapper }, projectImage),
React.createElement(ErrorManager, { ...errorDisplayConfig },
React.createElement(Box, { sx: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
}, className: defaultClasses.inputFieldsWrapper, "data-testid": defaultClasses.inputFieldsWrapper },
React.createElement(Box, { sx: {
width: '100%',
mb: username.length > 0 && !isUsernameValid && shouldValidateUsername
? 4
: `${(parseInt(theme.spacing(4)) + HELPER_TEXT_HEIGHT).toString()}px`,
[theme.breakpoints.down('sm')]: {
mb: username.length > 0 && !isUsernameValid && shouldValidateUsername
? 3
: `${(parseInt(theme.spacing(3)) + HELPER_TEXT_HEIGHT).toString()}px`,
},
} },
React.createElement(TextField, { fullWidth: true, id: "username", className: defaultClasses.usernameTextField, "data-testid": defaultClasses.usernameTextField, label: usernameLabel || 'Username', name: "username", variant: "filled", value: username, error: shouldValidateUsername && !isUsernameValid, helperText: shouldValidateUsername && !isUsernameValid ? usernameError : '', ...usernameTextFieldProps, onChange: (e) => {
// eslint-disable-next-line no-unused-expressions
usernameTextFieldProps?.onChange && usernameTextFieldProps.onChange(e);
handleUsernameInputChange(e.target.value);
}, onSubmit: (e) => {
// eslint-disable-next-line no-unused-expressions
usernameTextFieldProps?.onSubmit && usernameTextFieldProps.onSubmit(e);
if (e.key === 'Enter' && passwordField.current)
passwordField.current.focus();
}, onBlur: (e) => {
// eslint-disable-next-line no-unused-expressions
usernameTextFieldProps?.onBlur && usernameTextFieldProps.onBlur(e);
setShouldValidateUsername(true);
}, onKeyUp: (e) => {
if (e.key === 'Enter' && passwordField.current)
passwordField.current.focus();
} })),
React.createElement(Box, { sx: {
width: '100%',
mb: username.length > 0 && !isPasswordValid && shouldValidatePassword
? 2
: `${(parseInt(theme.spacing(2)) + HELPER_TEXT_HEIGHT).toString()}px`,
} },
React.createElement(PasswordTextField, { fullWidth: true, inputRef: passwordField, id: "password", className: defaultClasses.passwordTextField, "data-testid": defaultClasses.passwordTextField, name: "password", label: passwordLabel || 'Password', variant: "filled", value: password, error: shouldValidatePassword && !isPasswordValid, helperText: shouldValidatePassword && !isPasswordValid ? passwordError : '', ...passwordTextFieldProps, onChange: (e) => {
// eslint-disable-next-line no-unused-expressions
passwordTextFieldProps?.onChange && passwordTextFieldProps.onChange(e);
handlePasswordInputChange(e.target.value);
}, onSubmit: (e) => {
// eslint-disable-next-line no-unused-expressions
passwordTextFieldProps?.onSubmit && passwordTextFieldProps.onSubmit(e);
handleLoginSubmit(e);
}, onBlur: (e) => {
// eslint-disable-next-line no-unused-expressions
passwordTextFieldProps?.onBlur && passwordTextFieldProps.onBlur(e);
setShouldValidatePassword(true);
}, onKeyUp: (e) => {
// eslint-disable-next-line no-unused-expressions
passwordTextFieldProps?.onSubmit && passwordTextFieldProps.onSubmit(e);
handleLoginSubmit(e);
} })))),
React.createElement(Box, { sx: {
display: 'flex',
alignItems: 'center',
width: '100%',
mt: 1,
mb: 5,
flexWrap: { md: 'nowrap', sm: 'wrap' },
flexDirection: { md: 'row', sm: 'column' },
justifyContent: { md: 'space-between', sm: 'center' },
}, className: defaultClasses.rememberMeLoginRowWrapper, "data-testid": defaultClasses.rememberMeLoginRowWrapper },
showRememberMe && (React.createElement(Box, { sx: {
display: 'flex',
alignItems: 'center',
ml: -1.5,
mr: { md: 1, sm: 0 },
}, className: defaultClasses.rememberMeWrapper, "data-testid": defaultClasses.rememberMeWrapper },
React.createElement(Checkbox, { color: "primary", checked: rememberMe, onChange: (e) => handleRememberMeChanged(e.target.checked), className: defaultClasses.rememberMeCheckbox, "data-testid": defaultClasses.rememberMeCheckbox }),
React.createElement(Typography, { variant: "body1", className: defaultClasses.rememberMeLabel, "data-testid": defaultClasses.rememberMeLabel }, rememberMeLabel || 'Remember Me'))),
React.createElement(Box, { sx: {
display: 'flex',
flex: 1,
justifyContent: 'flex-end',
width: showRememberMe ? 'auto' : '100%',
}, className: defaultClasses.loginButtonWrapper, "data-testid": defaultClasses.loginButtonWrapper },
React.createElement(Button, { className: defaultClasses.loginButton, "data-testid": defaultClasses.loginButton, onClick: handleLogin, disabled: !isFormValid(), variant: "contained", color: "primary", sx: {
width: showRememberMe ? 150 : '100%',
} }, loginButtonLabel || 'Log In'))),
showForgotPassword && (React.createElement(Box, { sx: { display: 'flex', justifyContent: 'center', textAlign: 'center' }, className: defaultClasses.forgotPasswordWrapper, "data-testid": defaultClasses.forgotPasswordWrapper },
React.createElement(Typography, { variant: "body2", sx: LinkStyles, onClick: handleForgotPassword, className: defaultClasses.forgotPasswordLabel, "data-testid": defaultClasses.forgotPasswordLabel }, forgotPasswordLabel || 'Forgot your password?'))),
showSelfRegistration && (React.createElement(Box, { sx: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
marginTop: 4,
textAlign: 'center',
}, className: defaultClasses.selfRegisterWrapper, "data-testid": defaultClasses.selfRegisterWrapper },
React.createElement(Typography, { variant: "body2", className: defaultClasses.selfRegisterInstructionLabel, "data-testid": defaultClasses.selfRegisterInstructionLabel }, selfRegisterInstructions || 'Need an account?'),
React.createElement(Typography, { variant: "body2", sx: LinkStyles, onClick: handleSelfRegister, className: defaultClasses.selfRegisterLabel, "data-testid": defaultClasses.selfRegisterLabel }, selfRegisterButtonLabel || 'Register now!'))),
showContactSupport && (React.createElement(Box, { sx: { display: 'flex', justifyContent: 'center', marginTop: 4, textAlign: 'center' }, className: defaultClasses.contactSupportWrapper, "data-testid": defaultClasses.contactSupportWrapper },
React.createElement(Typography, { variant: "body2", sx: LinkStyles, onClick: handleContactSupport, className: defaultClasses.contactSupportLabel, "data-testid": defaultClasses.contactSupportLabel }, contactSupportLabel || 'Contact Support'))),
React.createElement(Box, { sx: { display: 'flex', justifyContent: 'center' } }, footer),
showCyberSecurityBadge && (React.createElement(Box, { sx: { display: 'flex', justifyContent: 'center', marginTop: 2 }, className: defaultClasses.cyberSecurityBadgeWrapper, "data-testid": defaultClasses.cyberSecurityBadgeWrapper },
React.createElement("img", { className: defaultClasses.cyberSecurityBadge, "data-testid": defaultClasses.cyberSecurityBadge, src: cyberSecurityBadge, alt: "Cyber Security Badge", style: { width: '100px' } }))))));
};