Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ describe('SectionList', () => {
within(incompleteSection).getByTestId('RadioButtonUncheckedIcon'),
).toBeInTheDocument();
});

it('renders the Complete/Incomplete title', () => {
const { getByTitle } = render(
<GoalCalculatorTestWrapper>
<SectionList sections={mockSections} />
</GoalCalculatorTestWrapper>,
);

expect(getByTitle('Complete')).toBeInTheDocument();
expect(getByTitle('Incomplete')).toBeInTheDocument();
});
});

describe('ReportSectionList', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,42 @@ import { useGoalCalculator } from '../Shared/GoalCalculatorContext';
interface ListItemContentProps {
title: string;
complete: boolean;
announceCompletion?: boolean;
}

const ListItemContent: React.FC<ListItemContentProps> = ({
title,
complete,
}) => (
<>
<CategoryListItemIcon
sx={(theme) => ({
color: complete
? theme.palette.mpdxBlue.main
: theme.palette.mpdxGrayDark.main,
})}
>
{complete ? <CircleIcon /> : <RadioButtonUncheckedIcon />}
</CategoryListItemIcon>
<ListItemText
primary={title}
primaryTypographyProps={{ variant: 'body2' }}
/>
</>
);
announceCompletion = false,
}) => {
const { t } = useTranslation();
const titleAccess = announceCompletion
? complete
? t('Complete')
: t('Incomplete')
: undefined;
return (
<>
<CategoryListItemIcon
sx={(theme) => ({
color: complete
? theme.palette.mpdxBlue.main
: theme.palette.mpdxGrayDark.main,
})}
>
{complete ? (
<CircleIcon titleAccess={titleAccess} />
) : (
<RadioButtonUncheckedIcon titleAccess={titleAccess} />
)}
</CategoryListItemIcon>
<ListItemText
primary={title}
primaryTypographyProps={{ variant: 'body2' }}
/>
</>
);
};

export interface SectionItem {
title: string;
Expand All @@ -50,7 +64,11 @@ export const SectionList: React.FC<SectionListProps> = ({ sections }) => {
<List disablePadding>
{sections.map(({ title, complete }, index) => (
<ListItem key={index} sx={CategoryListItemStyles}>
<ListItemContent title={title} complete={complete} />
<ListItemContent
title={title}
complete={complete}
announceCompletion
/>
Comment thread
wjames111 marked this conversation as resolved.
</ListItem>
))}
</List>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react';
import React, { useEffect, useMemo, useRef } from 'react';
import {
Button,
CircularProgress,
Expand Down Expand Up @@ -50,26 +50,31 @@
}
}, [open]);

const formTypeOptions: Array<{
value: DesignationSupportFormType;
title: string;
description: string;
}> = [
{
value: DesignationSupportFormType.Detailed,
title: t('Default'),
description: t(
'Full calculator with reimbursable expenses and 403b contributions.',
),
},
{
value: DesignationSupportFormType.Simple,
title: t('Simple'),
description: t(
'Streamlined calculator without reimbursable expenses or 403b contributions.',
),
},
];
const formTypeOptions = useMemo<
Array<{
value: DesignationSupportFormType;
title: string;
description: string;
}>
>(
() => [
{
value: DesignationSupportFormType.Detailed,
title: t('Default'),
description: t(
'Full calculator with reimbursable expenses and 403b contributions.',
),
},
{
value: DesignationSupportFormType.Simple,
title: t('Simple'),
description: t(
'Streamlined calculator without reimbursable expenses or 403b contributions.',
),
},
],
[t],
);

Check warning on line 77 in src/components/HrTools/PdsGoalCalculator/GoalsList/CreateGoalDialog.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Large Method

CreateGoalDialog:React.FC<CreateGoalDialogProps> increases from 130 to 135 lines of code, threshold = 100. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.

return (
<Dialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ import { PdsGoalCalculatorTestWrapper } from '../PdsGoalCalculatorTestWrapper';
import { PdsGoalsList } from './PdsGoalsList';

const mutationSpy = jest.fn();
const mockEnqueue = jest.fn();

jest.mock('notistack', () => ({
...jest.requireActual('notistack'),
useSnackbar: () => ({ enqueueSnackbar: mockEnqueue }),
}));

type FindByRole = ReturnType<typeof render>['findByRole'];

Expand Down Expand Up @@ -210,7 +204,7 @@ describe('PdsGoalsList', () => {
});

it('shows an error and skips mutation when reimbursement constants are missing for a Default goal', async () => {
const { findByRole } = render(
const { findByRole, findByText } = render(
<PdsGoalCalculatorTestWrapper
withProvider={false}
onCall={mutationSpy}
Expand All @@ -223,12 +217,11 @@ describe('PdsGoalsList', () => {
await openCreateGoalDialog(findByRole);
await submitFormType(findByRole, 'Default');

await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith(
expect(
await findByText(
'Could not load required defaults. Please try again or pick Simple.',
{ variant: 'error' },
),
);
).toBeInTheDocument();
expect(mutationSpy).not.toHaveGraphqlOperation('CreatePdsGoalCalculation');
await waitFor(() =>
expect(findByRole('button', { name: 'Create' })).resolves.toBeEnabled(),
Expand Down
25 changes: 20 additions & 5 deletions src/components/HrTools/PdsGoalCalculator/PdsGoalCalculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import {
AutosaveForm,
useAutosaveForm,
} from 'src/components/Shared/Autosave/AutosaveForm';
import { SectionList } from '../GoalCalculator/SharedComponents/SectionList';
import { PdsGoalCalculatorStepEnum } from './PdsGoalCalculatorHelper';
import { ReimbursableExpensesSectionList } from './ReimbursableExpenses/ReimbursableExpensesSectionList';
import { ReimbursableExpensesStep } from './ReimbursableExpenses/ReimbursableExpensesStep';
import { SetupSectionList } from './Setup/SetupSectionList';
import { SetupStep } from './Setup/SetupStep';
import { usePdsGoalCalculator } from './Shared/PdsGoalCalculatorContext';
import { PdsGoalCalculatorLayout } from './Shared/PdsGoalCalculatorLayout';
import { SummaryReportSectionList } from './SummaryReport/SummaryReportSectionList';
import { SummaryReportStep } from './SummaryReport/SummaryReportStep';
import { SupportItemSectionList } from './SupportItem/SupportItemSectionList';
import { SupportItemStep } from './SupportItem/SupportItemStep';

const CurrentStep: React.FC = () => {
Expand All @@ -28,6 +31,21 @@ const CurrentStep: React.FC = () => {
}
};

const CurrentSectionList: React.FC = () => {
const { currentStep } = usePdsGoalCalculator();

switch (currentStep.step) {
case PdsGoalCalculatorStepEnum.Setup:
return <SetupSectionList />;
case PdsGoalCalculatorStepEnum.ReimbursableExpenses:
return <ReimbursableExpensesSectionList />;
case PdsGoalCalculatorStepEnum.SupportItem:
return <SupportItemSectionList />;
case PdsGoalCalculatorStepEnum.SummaryReport:
return <SummaryReportSectionList />;
}
Comment thread
wjames111 marked this conversation as resolved.
};

const MainContent: React.FC = () => {
const { currentStep, stepIndex, steps, handleContinue, handlePreviousStep } =
usePdsGoalCalculator();
Expand All @@ -52,12 +70,9 @@ const MainContent: React.FC = () => {
};

export const PdsGoalCalculator: React.FC = () => {
const { currentStep } = usePdsGoalCalculator();
const sections = currentStep.sections;

return (
<PdsGoalCalculatorLayout
sectionListPanel={<SectionList sections={sections} />}
sectionListPanel={<CurrentSectionList />}
mainContent={
<AutosaveForm>
<MainContent />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { render, waitFor, within } from '@testing-library/react';
import { PdsGoalCalculatorTestWrapper } from '../PdsGoalCalculatorTestWrapper';
import { ReimbursableExpensesSectionList } from './ReimbursableExpensesSectionList';

const allReimbursableUntouched = {
ministryCellPhone: null,
ministryInternet: null,
mpdNewsletter: null,
mpdMiscellaneous: null,
accountTransfers: null,
otherMonthlyReimbursements: null,
conferenceRetreatCosts: null,
ministryTravelMeals: null,
otherAnnualReimbursements: null,
};

describe('ReimbursableExpensesSectionList', () => {
it('renders both sections as incomplete when no reimbursable fields have been touched', async () => {
const { findAllByRole } = render(
<PdsGoalCalculatorTestWrapper calculationMock={allReimbursableUntouched}>
<ReimbursableExpensesSectionList />
</PdsGoalCalculatorTestWrapper>,
);

const items = await findAllByRole('listitem');
expect(items).toHaveLength(2);

const [monthly, annual] = items;
expect(monthly).toHaveTextContent('Monthly Reimbursable Expenses');
expect(annual).toHaveTextContent('Annual Reimbursable Expenses');

await waitFor(() => {
expect(
within(monthly).getByTestId('RadioButtonUncheckedIcon'),
).toBeInTheDocument();
expect(
within(annual).getByTestId('RadioButtonUncheckedIcon'),
).toBeInTheDocument();
});
});

it('marks only Monthly complete when a monthly field is touched', async () => {
const { findAllByRole } = render(
<PdsGoalCalculatorTestWrapper
calculationMock={{ ...allReimbursableUntouched, ministryCellPhone: 25 }}
>
<ReimbursableExpensesSectionList />
</PdsGoalCalculatorTestWrapper>,
);

const [monthly, annual] = await findAllByRole('listitem');
await waitFor(() => {
expect(within(monthly).getByTestId('CircleIcon')).toBeInTheDocument();
expect(
within(annual).getByTestId('RadioButtonUncheckedIcon'),
).toBeInTheDocument();
});
});

it('marks only Annual complete when an annual field is touched', async () => {
const { findAllByRole } = render(
<PdsGoalCalculatorTestWrapper
calculationMock={{
...allReimbursableUntouched,
conferenceRetreatCosts: 100,
}}
>
<ReimbursableExpensesSectionList />
</PdsGoalCalculatorTestWrapper>,
);

const [monthly, annual] = await findAllByRole('listitem');
await waitFor(() => {
expect(
within(monthly).getByTestId('RadioButtonUncheckedIcon'),
).toBeInTheDocument();
expect(within(annual).getByTestId('CircleIcon')).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SectionList } from '../../GoalCalculator/SharedComponents/SectionList';
import { usePdsGoalCalculator } from '../Shared/PdsGoalCalculatorContext';
import {
isAnnualReimbursableComplete,
isMonthlyReimbursableComplete,
} from '../Shared/pdsCompletion';

export const ReimbursableExpensesSectionList: React.FC = () => {
const { t } = useTranslation();
const { calculation } = usePdsGoalCalculator();

return (
<SectionList
sections={[
{
title: t('Monthly Reimbursable Expenses'),
complete: isMonthlyReimbursableComplete(calculation),
},
{
title: t('Annual Reimbursable Expenses'),
complete: isAnnualReimbursableComplete(calculation),
},
]}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { render, waitFor, within } from '@testing-library/react';
import { PdsGoalCalculatorTestWrapper } from '../PdsGoalCalculatorTestWrapper';
import { SetupSectionList } from './SetupSectionList';

describe('SetupSectionList', () => {
it('renders the Setup section as complete when calculation has all required fields', async () => {
const { findByRole } = render(
<PdsGoalCalculatorTestWrapper>
<SetupSectionList />
</PdsGoalCalculatorTestWrapper>,
);

const setupItem = await findByRole('listitem');
expect(setupItem).toHaveTextContent('Setup');
await waitFor(() =>
expect(within(setupItem).getByTestId('CircleIcon')).toBeInTheDocument(),
);
});

it('renders the Setup section as incomplete when calculation is missing required fields', async () => {
const { findByRole } = render(
<PdsGoalCalculatorTestWrapper calculationMock={{ name: '' }}>
<SetupSectionList />
</PdsGoalCalculatorTestWrapper>,
);

const setupItem = await findByRole('listitem');
expect(setupItem).toHaveTextContent('Setup');
await waitFor(() =>
expect(
within(setupItem).getByTestId('RadioButtonUncheckedIcon'),
).toBeInTheDocument(),
);
});
});
Loading
Loading