UNPKG

@brightlayer-ui/react-auth-workflow

Version:

Re-usable workflow components for Authentication and Registration within Eaton applications.

216 lines (215 loc) 15.6 kB
/* 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' } })))))); };