Skip to content

Commit

Permalink
Merge pull request #480 from etn-ccis/4701---reset-password-screen-fixes
Browse files Browse the repository at this point in the history
Reset Passoword: Fix showSuccessScreen, slotProps, slots props
  • Loading branch information
surajeaton authored Sep 27, 2023
2 parents c72b17d + 9ae57dd commit 414c646
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 71 deletions.
1 change: 1 addition & 0 deletions login-workflow/shared-auth
Submodule shared-auth added at 792a79
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export const ForgotPasswordScreen: React.FC<ForgotPasswordScreenProps> = (props)
onNext: (): void => {
navigate(routeConfig.LOGIN);
if (slotProps.SuccessScreen.WorkflowCardActionsProps)
slotProps.SuccessScreen.WorkflowCardActionsProps.onNext();
slotProps.SuccessScreen.WorkflowCardActionsProps?.onNext?.();
},
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import '@testing-library/jest-dom';
import { cleanup, render, screen, fireEvent, RenderResult } from '@testing-library/react';
import { cleanup, render, screen, fireEvent, RenderResult, waitFor } from '@testing-library/react';
import { ResetPasswordScreen } from './ResetPasswordScreen';
import { AuthContextProvider } from '../../contexts';
import { ResetPasswordScreenProps } from './types';
Expand Down Expand Up @@ -33,30 +33,44 @@ describe('Reset Password Screen', () => {

it('renders without crashing', () => {
renderer();

expect(screen.getByText('Reset Password')).toBeInTheDocument();
});

it('should update values when passed as props', () => {
renderer({ WorkflowCardHeaderProps: { title: 'Test Title' } });

expect(screen.queryByText('Reset Password')).toBeNull();
expect(screen.getByText('Test Title')).toBeInTheDocument();
});

it('should show success screen, when showSuccessScreen prop is true', () => {
renderer({ showSuccessScreen: true });
it('should show success screen, when okay button is clicked', async () => {
const { getByLabelText } = renderer({
showSuccessScreen: true,
PasswordProps: {
newPasswordLabel: 'New Password',
confirmPasswordLabel: 'Confirm New Password',
onPasswordChange: jest.fn(),
passwordRequirements: [],
},
WorkflowCardActionsProps: {
canGoNext: true,
nextLabel: 'Next',
},
});

expect(screen.getByText('Your password was successfully reset.')).toBeInTheDocument();
const newPasswordInput = getByLabelText('New Password');
const confirmPasswordInput = getByLabelText('Confirm New Password');
fireEvent.change(newPasswordInput, { target: { value: 'Abc@1234' } });
fireEvent.change(confirmPasswordInput, { target: { value: 'Abc@1234' } });
fireEvent.click(screen.getByText('Next'));
await waitFor(() => expect(screen.getByText('Your password was successfully reset.')));
});

it('should show loader, when loading prop is passed to WorkflowCardBaseProps', () => {
renderer({ WorkflowCardBaseProps: { loading: true } });

expect(screen.getByTestId('blui-spinner')).toBeInTheDocument();
});

it('should call onNext, when Next button clicked', () => {
it('should call onNext, when Next button clicked', async () => {
const { getByLabelText } = renderer({
WorkflowCardActionsProps: {
onNext: mockOnNext(),
Expand All @@ -67,17 +81,15 @@ describe('Reset Password Screen', () => {

const passwordField = getByLabelText('New Password');
const confirmPasswordField = getByLabelText('Confirm New Password');

fireEvent.change(passwordField, { target: { value: 'Abcd@123' } });
fireEvent.blur(passwordField);
fireEvent.change(confirmPasswordField, { target: { value: 'Abcd@123' } });
fireEvent.blur(confirmPasswordField);

const nextButton = screen.getByText('Next');
expect(nextButton).toBeInTheDocument();
expect(screen.getByText(/Next/i)).toBeEnabled();
fireEvent.click(nextButton);
expect(mockOnNext).toHaveBeenCalled();
await waitFor(() => expect(mockOnNext).toHaveBeenCalled());
});

it('should call onPrevious, when Back button clicked', () => {
Expand All @@ -95,25 +107,4 @@ describe('Reset Password Screen', () => {
fireEvent.click(backButton);
expect(mockOnPrevious).toHaveBeenCalled();
});

it('should call onNext, when Done button is clicked on success screen', () => {
renderer({
showSuccessScreen: true,
slotProps: {
SuccessScreen: {
messageTitle: 'Success',
WorkflowCardActionsProps: {
showPrevious: false,
fullWidthButton: true,
showNext: true,
nextLabel: 'Done',
onNext: mockOnNext(),
},
},
},
});

expect(screen.getByText('Done')).toBeInTheDocument();
expect(mockOnNext).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,24 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
const { t } = useLanguageLocale();
const passwordRef = useRef(null);
const confirmRef = useRef(null);
const [passwordInput, setPasswordInput] = useState('');
const [confirmInput, setConfirmInput] = useState('');
const { triggerError, errorManagerConfig } = useErrorManager();

const {
WorkflowCardBaseProps,
WorkflowCardHeaderProps,
WorkflowCardInstructionProps,
WorkflowCardActionsProps,
PasswordProps,
errorDisplayConfig = errorManagerConfig,
slots,
slotProps,
} = props;

const [passwordInput, setPasswordInput] = useState(PasswordProps?.initialNewPasswordValue ?? '');
const [confirmInput, setConfirmInput] = useState(PasswordProps?.initialConfirmPasswordValue ?? '');
const [hasVerifyCodeError, setHasVerifyCodeError] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [showSuccessScreen, setShowSuccessScreen] = useState(props.showSuccessScreen);
const { triggerError, errorManagerConfig } = useErrorManager();
const [showSuccessScreen, setShowSuccessScreen] = useState(false);

const { code, email } = parseQueryString(window.location.search);

Expand All @@ -60,20 +72,27 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
try {
setIsLoading(true);
await actions.setPassword(code, passwordInput, email);
setShowSuccessScreen(true);
if (props.showSuccessScreen === false) {
navigate(routeConfig.LOGIN);
} else {
setShowSuccessScreen(true);
}
} catch (_error) {
triggerError(_error as Error);
} finally {
setIsLoading(false);
}
}, [actions, code, passwordInput, email, triggerError]);
}, [actions, code, passwordInput, email, triggerError, props.showSuccessScreen, navigate, routeConfig]);

const areValidMatchingPasswords = useCallback((): boolean => {
if (PasswordProps?.passwordRequirements?.length === 0) {
return confirmInput === passwordInput;
}
for (let i = 0; i < passwordRequirements.length; i++) {
if (!new RegExp(passwordRequirements[i].regex).test(passwordInput)) return false;
}
return confirmInput === passwordInput;
}, [passwordRequirements, passwordInput, confirmInput]);
}, [PasswordProps?.passwordRequirements?.length, passwordRequirements, passwordInput, confirmInput]);

const updateFields = useCallback(
(fields: { password: string; confirm: string }) => {
Expand All @@ -89,15 +108,6 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const {
WorkflowCardBaseProps,
WorkflowCardHeaderProps,
WorkflowCardInstructionProps,
WorkflowCardActionsProps,
PasswordProps,
errorDisplayConfig = errorManagerConfig,
} = props;

const workflowCardBaseProps = {
loading: isLoading,
...WorkflowCardBaseProps,
Expand All @@ -118,7 +128,7 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
showPrevious: true,
nextLabel: t('bluiCommon:ACTIONS.NEXT'),
previousLabel: t('bluiCommon:ACTIONS.BACK'),
canGoNext: passwordInput !== '' && confirmInput !== '' && passwordInput === confirmInput,
canGoNext: passwordInput !== '' && confirmInput !== '' && areValidMatchingPasswords(),
...WorkflowCardActionsProps,
onNext: (): void => {
void handleOnNext();
Expand All @@ -134,7 +144,7 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
newPasswordLabel: t('bluiAuth:CHANGE_PASSWORD.NEW_PASSWORD'),
confirmPasswordLabel: t('bluiAuth:CHANGE_PASSWORD.CONFIRM_NEW_PASSWORD'),
passwordNotMatchError: t('bluiCommon:FORMS.PASS_MATCH_ERROR'),
passwordRequirements: passwordRequirements,
passwordRequirements: PasswordProps?.passwordRequirements ?? passwordRequirements,
passwordRef,
confirmRef,
...PasswordProps,
Expand All @@ -158,14 +168,13 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
WorkflowCardInstructionProps={workflowCardInstructionProps}
WorkflowCardActionsProps={workflowCardActionsProps}
PasswordProps={passwordProps}
showSuccessScreen={showSuccessScreen}
slots={slots}
slotProps={{
SuccessScreen: {
icon: <CheckCircle color="primary" sx={{ fontSize: 100 }} />,
messageTitle: t('bluiAuth:PASSWORD_RESET.SUCCESS_MESSAGE'),
message: t('bluiAuth:CHANGE_PASSWORD.SUCCESS_MESSAGE'),
onDismiss: (): void => {
navigate(routeConfig.LOGIN);
},
WorkflowCardActionsProps: {
showPrevious: false,
fullWidthButton: true,
Expand All @@ -176,8 +185,8 @@ export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = (props) =
},
},
},
...slotProps,
}}
showSuccessScreen={showSuccessScreen}
errorDisplayConfig={{
...errorDisplayConfig,
onClose: hasVerifyCodeError
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import {
SetPassword,
WorkflowCardActions,
} from '../../components';
import { SuccessScreenBase } from '../SuccessScreen/SuccessScreenBase';
import { SuccessScreenProps } from '../SuccessScreen';
import ErrorManager from '../../components/Error/ErrorManager';
import { SuccessScreenBase, SuccessScreenProps } from '../SuccessScreen';

/**
* Component that renders a ResetPassword screen that allows a user to reset their password and shows a success message upon a successful password reset..
Expand All @@ -37,25 +36,29 @@ export const ResetPasswordScreenBase: React.FC<React.PropsWithChildren<ResetPass
const instructionsProps = props.WorkflowCardInstructionProps || {};
const actionsProps = props.WorkflowCardActionsProps || {};
const passwordProps = props.PasswordProps || { onPasswordChange: () => ({}) };
const { showSuccessScreen, slots, slotProps, errorDisplayConfig } = props;
const { showSuccessScreen, slots, slotProps = {}, errorDisplayConfig } = props;

const getSuccessScreen = (
_props: SuccessScreenProps,
SuccessScreen?: (props: SuccessScreenProps) => JSX.Element
): JSX.Element => (SuccessScreen ? SuccessScreen(_props) : <SuccessScreenBase {..._props} />);

return showSuccessScreen ? (
getSuccessScreen(slotProps?.SuccessScreen, slots?.SuccessScreen)
) : (
<WorkflowCard {...cardBaseProps}>
<WorkflowCardHeader {...headerProps} />
<WorkflowCardInstructions {...instructionsProps} />
<WorkflowCardBody>
<ErrorManager {...errorDisplayConfig}>
<SetPassword {...passwordProps} />
</ErrorManager>
</WorkflowCardBody>
<WorkflowCardActions {...actionsProps} />
</WorkflowCard>
return (
<>
{showSuccessScreen ? (
getSuccessScreen(slotProps?.SuccessScreen, slots?.SuccessScreen)
) : (
<WorkflowCard {...cardBaseProps}>
<WorkflowCardHeader {...headerProps} />
<WorkflowCardInstructions {...instructionsProps} divider />
<WorkflowCardBody>
<ErrorManager {...errorDisplayConfig}>
<SetPassword {...passwordProps} />
</ErrorManager>
</WorkflowCardBody>
<WorkflowCardActions {...actionsProps} divider />
</WorkflowCard>
)}
</>
);
};

0 comments on commit 414c646

Please sign in to comment.