From 091612ac1074bad1ea2870677ee5a22e08b33053 Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Fri, 21 Mar 2025 10:42:13 -0500 Subject: [PATCH 01/11] Warn if the goal is below the machine-calculated goal --- .../MonthlyGoalAccordion.test.tsx | 148 ++++++++++++++---- .../MonthlyGoalAccordion.tsx | 91 +++++++++-- .../Shared/Forms/Accordions/AccordionItem.tsx | 2 +- src/components/Shared/Forms/FieldWrapper.tsx | 10 +- 4 files changed, 201 insertions(+), 50 deletions(-) diff --git a/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.test.tsx b/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.test.tsx index 43b890adcd..cb30828338 100644 --- a/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.test.tsx +++ b/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.test.tsx @@ -35,9 +35,9 @@ const mutationSpy = jest.fn(); interface ComponentsProps { monthlyGoal: number | null; - monthlyGoalUpdatedAt?: string; + monthlyGoalUpdatedAt?: string | null; machineCalculatedGoal?: number; - machineCalculatedGoalCurrency?: string; + machineCalculatedGoalCurrency?: string | null; expandedAccordion: PreferenceAccordion | null; } @@ -85,34 +85,128 @@ describe('MonthlyGoalAccordion', () => { mutationSpy.mockClear(); }); - it('should render accordion closed', () => { - const { getByTestId, getByText, queryByRole } = render( - , - ); + describe('closed', () => { + it('renders label and hides the textbox', () => { + const { getByText, queryByRole } = render( + , + ); - expect(getByTestId('AccordionSummaryValue')).toHaveTextContent( - '$100 (last updated Jan 1, 2024)', - ); - expect(getByText(label)).toBeInTheDocument(); - expect(queryByRole('textbox')).not.toBeInTheDocument(); - }); + expect(getByText(label)).toBeInTheDocument(); + expect(queryByRole('textbox')).not.toBeInTheDocument(); + }); - it('should render accordion closed with calculated goal', async () => { - const { findByText, queryByRole } = render( - , - ); + it('renders goal without updated date', () => { + const { getByTestId } = render( + , + ); + + expect(getByTestId('AccordionSummaryValue')).toHaveTextContent('$100'); + }); - expect(await findByText('€1,000 (estimated)')).toBeInTheDocument(); - expect(queryByRole('textbox')).not.toBeInTheDocument(); + it('renders goal and updated date', () => { + const { getByTestId } = render( + , + ); + + expect(getByTestId('AccordionSummaryValue')).toHaveTextContent( + '$100 (last updated Jan 1, 2024)', + ); + }); + + it('renders too low warning', async () => { + const { getByTestId } = render( + , + ); + + await waitFor(() => + expect(getByTestId('AccordionSummaryValue')).toHaveTextContent( + '$100 (below machine-calculated support goal)', + ), + ); + }); + + it('hides too low warning when currencies do not match', async () => { + const { getByTestId } = render( + , + ); + + await waitFor(() => + expect(getByTestId('AccordionSummaryValue')).not.toHaveTextContent( + /below machine-calculated support goal/, + ), + ); + }); + + it('renders calculated goal', async () => { + const { getByTestId } = render( + , + ); + + await waitFor(() => + expect(getByTestId('AccordionSummaryValue')).toHaveTextContent( + '€1,000 (estimated)', + ), + ); + }); + + it('renders calculated goal without currency', async () => { + const { getByTestId } = render( + , + ); + + await waitFor(() => + expect(getByTestId('AccordionSummaryValue')).toHaveTextContent( + '1,000 (estimated)', + ), + ); + }); + + it('renders only goal when calculated goal is missing', async () => { + const { getByTestId } = render( + , + ); + + await waitFor(() => + expect(getByTestId('AccordionSummaryValue')).toHaveTextContent('$100'), + ); + }); + + it('renders nothing when goal and calculated goal are missing', async () => { + const { queryByTestId } = render( + , + ); + + await waitFor(() => + expect(queryByTestId('AccordionSummaryValue')).not.toBeInTheDocument(), + ); + }); }); it('should render accordion open and textfield should have a value', () => { diff --git a/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.tsx b/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.tsx index bb5335f796..b391d815d9 100644 --- a/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.tsx +++ b/src/components/Settings/preferences/accordions/MonthlyGoalAccordion/MonthlyGoalAccordion.tsx @@ -1,5 +1,6 @@ import React, { ReactElement, useMemo } from 'react'; -import { Box, Button, TextField, Tooltip } from '@mui/material'; +import WarningIcon from '@mui/icons-material/Warning'; +import { Box, Button, TextField, Tooltip, Typography } from '@mui/material'; import { Formik } from 'formik'; import { DateTime } from 'luxon'; import { useSnackbar } from 'notistack'; @@ -10,7 +11,7 @@ import { AccordionItem } from 'src/components/Shared/Forms/Accordions/AccordionI import { FieldWrapper } from 'src/components/Shared/Forms/FieldWrapper'; import { AccountListSettingsInput } from 'src/graphql/types.generated'; import { useLocale } from 'src/hooks/useLocale'; -import { currencyFormat, dateFormat } from 'src/lib/intlFormat'; +import { currencyFormat, dateFormat, numberFormat } from 'src/lib/intlFormat'; import { AccordionProps } from '../../../accordionHelper'; import { useUpdateAccountPreferencesMutation } from '../UpdateAccountPreferences.generated'; import { useMachineCalculatedGoalQuery } from './MachineCalculatedGoal.generated'; @@ -33,7 +34,7 @@ const formatMonthlyGoal = ( if (currency) { return currencyFormat(goal, currency, locale); } - return goal.toString(); + return numberFormat(goal, locale); }; interface MonthlyGoalAccordionProps @@ -81,18 +82,48 @@ export const MonthlyGoalAccordion: React.FC = ({ [calculatedGoal, calculatedCurrency, locale], ); - const formattedMonthlyGoal = useMemo(() => { + const accordionValue = useMemo(() => { const goal = formatMonthlyGoal(initialMonthlyGoal, currency, locale); - if (!goal || !monthlyGoalUpdatedAt) { + + if (initialMonthlyGoal === null) { + if (typeof calculatedGoal === 'number') { + // There is no preferences goal, but there is a machine-calculated goal + return t('{{goal}} (estimated)', { goal: formattedCalculatedGoal }); + } else { + // There is no preferences goal or machine-calculated goal + return null; + } + } else if ( + typeof currency === 'string' && + currency === calculatedCurrency && + typeof calculatedGoal === 'number' && + initialMonthlyGoal < calculatedGoal + ) { + // Staff-entered goal is below machine-calculated goal + return ( + + {t('{{goal}} (below machine-calculated support goal)', { goal })} + + ); + } else if (monthlyGoalUpdatedAt) { + // Staff-entered goal has an updated date + const date = DateTime.fromISO(monthlyGoalUpdatedAt); + return t('{{goal}} (last updated {{updated}})', { + goal, + updated: dateFormat(date, locale), + }); + } else { + // Staff-entered goal does not have an updated date return goal; } - - const date = DateTime.fromISO(monthlyGoalUpdatedAt); - return t('{{goal}} (last updated {{updated}})', { - goal, - updated: dateFormat(date, locale), - }); - }, [initialMonthlyGoal, monthlyGoalUpdatedAt, currency, locale]); + }, [ + initialMonthlyGoal, + calculatedGoal, + formattedCalculatedGoal, + monthlyGoalUpdatedAt, + currency, + locale, + ]); const onSubmit = async ( attributes: Pick, @@ -142,15 +173,36 @@ export const MonthlyGoalAccordion: React.FC = ({ } }; + const getWarning = (currentGoal: number | null) => { + if ( + typeof currentGoal === 'number' && + typeof calculatedGoal === 'number' && + currentGoal < calculatedGoal + ) { + return ( + + + {t( + 'Your current monthly goal is less than the amount NetSuite estimates that you need. Please review your goal and adjust it if needed.', + )} + + ); + } + }; + return ( @@ -172,7 +224,14 @@ export const MonthlyGoalAccordion: React.FC = ({ handleChange, }): ReactElement => (
- + + {getInstructions()} + {getWarning(monthlyGoal)} + + } + > { onAccordionChange: (accordion: AccordionEnum | null) => void; expandedAccordion: AccordionEnum | null; label: string; - value: string; + value: React.ReactNode; children?: React.ReactNode; fullWidth?: boolean; image?: React.ReactNode; diff --git a/src/components/Shared/Forms/FieldWrapper.tsx b/src/components/Shared/Forms/FieldWrapper.tsx index 9ddb0d82fb..83b94e990b 100644 --- a/src/components/Shared/Forms/FieldWrapper.tsx +++ b/src/components/Shared/Forms/FieldWrapper.tsx @@ -9,7 +9,7 @@ import { interface FieldWrapperProps { labelText?: string; - helperText?: string; + helperText?: React.ReactNode; helperPosition?: HelperPositionEnum; formControlDisabled?: FormControlProps['disabled']; formControlError?: FormControlProps['error']; @@ -22,7 +22,7 @@ interface FieldWrapperProps { export const FieldWrapper: React.FC = ({ labelText = '', - helperText = '', + helperText = null, helperPosition = HelperPositionEnum.Top, formControlDisabled = false, formControlError = false, @@ -47,12 +47,10 @@ export const FieldWrapper: React.FC = ({ '' ); - const helperTextOutput = helperText ? ( + const helperTextOutput = helperText && ( - {t(helperText)} + {helperText} - ) : ( - '' ); return ( From b348d96a81c086fcb252be1df5128c09320ebecd Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Fri, 21 Mar 2025 14:03:00 -0500 Subject: [PATCH 02/11] Add low goal, old goal, and machine-calculated goal annotations to dashboard --- src/components/AccountLists/AccountLists.tsx | 8 +- .../MonthlyGoal/MonthlyGoal.test.tsx | 40 +++++++++- .../Dashboard/MonthlyGoal/MonthlyGoal.tsx | 76 ++++++++++++++++--- 3 files changed, 110 insertions(+), 14 deletions(-) diff --git a/src/components/AccountLists/AccountLists.tsx b/src/components/AccountLists/AccountLists.tsx index d34ffc7b4f..d37085e6f1 100644 --- a/src/components/AccountLists/AccountLists.tsx +++ b/src/components/AccountLists/AccountLists.tsx @@ -132,7 +132,10 @@ const AccountLists = ({ data }: Props): ReactElement => { label: t('Last updated {{date}}', { date: dateFormat(preferencesGoalDate, locale), }), - variant: 'body2', + color: + preferencesGoalDate <= DateTime.now().minus({ year: 1 }) + ? 'statusWarning.main' + : undefined, } : null; const annotationId = `annotation-${id}`; @@ -182,6 +185,7 @@ const AccountLists = ({ data }: Props): ReactElement => { {currencyFormat( @@ -238,7 +242,7 @@ const AccountLists = ({ data }: Props): ReactElement => { id={annotationId} component="div" color={annotation.color} - variant={annotation.variant} + variant="body2" > * {annotation.label} diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx index beeddb3cd0..0a33211567 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx @@ -26,7 +26,13 @@ const Components = ({ mocks={{ HealthIndicator: { accountList: { - healthIndicatorData, + healthIndicatorData: + healthIndicatorData === null + ? null + : { + machineCalculatedGoalCurrency: 'USD', + ...healthIndicatorData, + }, }, }, }} @@ -301,7 +307,37 @@ describe('MonthlyGoal', () => { }); }); - it('should set the monthly goal to the machine calculated goal', async () => { + describe('below machine-calculated warning', () => { + it('is shown if goal is less than the machine-calculated goal', async () => { + const { findByText } = render( + , + ); + + expect( + await findByText('Below machine-calculated goal'), + ).toBeInTheDocument(); + }); + + it('is hidden if goal is greater than or equal to the machine-calculated goal', async () => { + const { queryByText } = render( + , + ); + + await waitFor(() => + expect( + queryByText('Below machine-calculated goal'), + ).not.toBeInTheDocument(), + ); + }); + }); + + it('should set the monthly goal to the machine-calculated goal', async () => { const { findByRole, findByLabelText, diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx index 071e82c1a0..6f3c983766 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx @@ -10,6 +10,7 @@ import { Theme, Tooltip, Typography, + TypographyProps, } from '@mui/material'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; @@ -53,6 +54,11 @@ const useStyles = makeStyles()((_theme: Theme) => ({ }, })); +interface Annotation { + label: string; + color: TypographyProps['color']; +} + export interface MonthlyGoalProps { accountListId: string; accountList: Pick< @@ -95,12 +101,21 @@ const MonthlyGoal = ({ const latestHealthIndicatorData = data?.accountList.healthIndicatorData; const showHealthIndicator = !!latestHealthIndicatorData; const machineCalculatedGoal = - latestHealthIndicatorData?.machineCalculatedGoal ?? null; + latestHealthIndicatorData?.machineCalculatedGoal && + typeof latestHealthIndicatorData.machineCalculatedGoalCurrency === + 'string' && + latestHealthIndicatorData.machineCalculatedGoalCurrency === currency + ? latestHealthIndicatorData.machineCalculatedGoal + : null; const goal = preferencesGoal ?? machineCalculatedGoal ?? 0; const preferencesGoalDate = typeof preferencesGoal === 'number' && preferencesGoalUpdatedAt && DateTime.fromISO(preferencesGoalUpdatedAt); + const preferencesGoalLow = + typeof preferencesGoal === 'number' && + typeof machineCalculatedGoal === 'number' && + preferencesGoal < machineCalculatedGoal; const receivedPercentage = received / goal; const pledgedPercentage = pledged / goal; const belowGoal = goal - pledged; @@ -133,7 +148,28 @@ const MonthlyGoal = ({ hIGrid: showHealthIndicator ? { xs: 12, md: 6, lg: 5 } : { xs: 0 }, }; - const lastUpdatedId = useId(); + const annotation: Annotation | null = preferencesGoalLow + ? { + label: t('Below machine-calculated goal'), + color: 'statusWarning.main', + } + : typeof preferencesGoal !== 'number' + ? { + label: t('Machine-calculated goal'), + color: 'statusWarning.main', + } + : preferencesGoalDate + ? { + label: t('Last updated {{date}}', { + date: dateFormat(preferencesGoalDate, locale), + }), + color: + preferencesGoalDate <= DateTime.now().minus({ year: 1 }) + ? 'statusWarning.main' + : 'textSecondary', + } + : null; + const annotationId = useId(); return ( <> @@ -190,7 +226,7 @@ const MonthlyGoal = ({
{loading ? ( ) : ( - currencyFormat(goal, currency, locale) + <> + {currencyFormat(goal, currency, locale)} + {annotation && ( + + * + + )} + )} - {preferencesGoalDate && ( - - {t('Last updated {{date}}', { - date: dateFormat(preferencesGoalDate, locale), - })} + {annotation && ( + + * + {annotation.label} )} - {preferencesGoal === null && ( + {(preferencesGoal === null || preferencesGoalLow) && ( From 4f01759c1b3e4a9dfdfbdf4d8a72a2e679d2bedf Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Fri, 21 Mar 2025 15:43:05 -0500 Subject: [PATCH 03/11] Extract HI logic into reusable module --- .../MonthlyGoal/MonthlyGoal.test.tsx | 19 +-- .../Dashboard/MonthlyGoal/MonthlyGoal.tsx | 61 +++---- src/lib/healthIndicator.test.ts | 159 ++++++++++++++++++ src/lib/healthIndicator.ts | 111 ++++++++++++ 4 files changed, 298 insertions(+), 52 deletions(-) create mode 100644 src/lib/healthIndicator.test.ts create mode 100644 src/lib/healthIndicator.ts diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx index 0a33211567..3570fa2fac 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.test.tsx @@ -338,28 +338,13 @@ describe('MonthlyGoal', () => { }); it('should set the monthly goal to the machine-calculated goal', async () => { - const { - findByRole, - findByLabelText, - getByRole, - queryByRole, - queryByText, - } = render( + const { findByRole, getByRole, queryByRole, queryByText } = render( , ); - expect( - await findByLabelText( - /^Your current goal of \$7,000 is machine-calculated/, - ), - ).toHaveStyle('color: rgb(211, 68, 0)'); - expect( await findByRole('heading', { name: '$7,000' }), ).toBeInTheDocument(); diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx index 6f3c983766..c1f724a130 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx @@ -12,7 +12,6 @@ import { Typography, TypographyProps, } from '@mui/material'; -import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; import { HealthIndicatorWidget } from 'src/components/Reports/HealthIndicatorReport/HealthIndicatorWidget/HealthIndicatorWidget'; @@ -23,6 +22,7 @@ import { StatusEnum, } from 'src/graphql/types.generated'; import { useLocale } from 'src/hooks/useLocale'; +import { GoalSource, getHealthIndicatorInfo } from 'src/lib/healthIndicator'; import { currencyFormat, dateFormat, @@ -85,8 +85,6 @@ const MonthlyGoal = ({ const loading = accountList === null; const { - monthlyGoal: preferencesGoal, - monthlyGoalUpdatedAt: preferencesGoalUpdatedAt, receivedPledges: received = 0, totalPledges: pledged = 0, currency, @@ -100,26 +98,21 @@ const MonthlyGoal = ({ const latestHealthIndicatorData = data?.accountList.healthIndicatorData; const showHealthIndicator = !!latestHealthIndicatorData; - const machineCalculatedGoal = - latestHealthIndicatorData?.machineCalculatedGoal && - typeof latestHealthIndicatorData.machineCalculatedGoalCurrency === - 'string' && - latestHealthIndicatorData.machineCalculatedGoalCurrency === currency - ? latestHealthIndicatorData.machineCalculatedGoal - : null; - const goal = preferencesGoal ?? machineCalculatedGoal ?? 0; - const preferencesGoalDate = - typeof preferencesGoal === 'number' && - preferencesGoalUpdatedAt && - DateTime.fromISO(preferencesGoalUpdatedAt); - const preferencesGoalLow = - typeof preferencesGoal === 'number' && - typeof machineCalculatedGoal === 'number' && - preferencesGoal < machineCalculatedGoal; - const receivedPercentage = received / goal; - const pledgedPercentage = pledged / goal; - const belowGoal = goal - pledged; - const belowGoalPercentage = belowGoal / goal; + const { + goal, + goalSource, + machineCalculatedGoal, + preferencesGoal, + preferencesGoalUpdatedAt, + preferencesGoalLow, + preferencesGoalOld, + } = getHealthIndicatorInfo(accountList, latestHealthIndicatorData); + const goalOrZero = goal ?? 0; + const hasValidGoal = goal !== null; + const receivedPercentage = hasValidGoal ? received / goal : NaN; + const pledgedPercentage = hasValidGoal ? pledged / goal : NaN; + const belowGoal = goalOrZero - pledged; + const belowGoalPercentage = hasValidGoal ? belowGoal / goal : NaN; const toolTipText = useMemo(() => { if (preferencesGoal) { @@ -153,20 +146,17 @@ const MonthlyGoal = ({ label: t('Below machine-calculated goal'), color: 'statusWarning.main', } - : typeof preferencesGoal !== 'number' + : goalSource === GoalSource.MachineCalculated ? { label: t('Machine-calculated goal'), color: 'statusWarning.main', } - : preferencesGoalDate + : preferencesGoalUpdatedAt ? { label: t('Last updated {{date}}', { - date: dateFormat(preferencesGoalDate, locale), + date: dateFormat(preferencesGoalUpdatedAt, locale), }), - color: - preferencesGoalDate <= DateTime.now().minus({ year: 1 }) - ? 'statusWarning.main' - : 'textSecondary', + color: preferencesGoalOld ? 'statusWarning.main' : 'textSecondary', } : null; const annotationId = useId(); @@ -195,7 +185,7 @@ const MonthlyGoal = ({ - {!loading && currencyFormat(goal, currency, locale)} + {!loading && currencyFormat(goalOrZero, currency, locale)} @@ -217,7 +207,7 @@ const MonthlyGoal = ({ ) : ( <> - {currencyFormat(goal, currency, locale)} + {currencyFormat(goalOrZero, currency, locale)} {annotation && ( )} - {(preferencesGoal === null || preferencesGoalLow) && ( + {(goalSource === GoalSource.MachineCalculated || + preferencesGoalLow) && ( - {calculatedGoal && initialMonthlyGoal !== null && ( - - - - )} + + + )} )} From 100da018498d647f8bc17bb3ff8bff2b5216c925 Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Mon, 24 Mar 2025 13:47:29 -0500 Subject: [PATCH 06/11] Add low goal warning to My Accounts grid --- .../AccountLists/AccountLists.test.tsx | 58 ++++++++++++++++++- src/components/AccountLists/AccountLists.tsx | 37 +++++++----- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/components/AccountLists/AccountLists.test.tsx b/src/components/AccountLists/AccountLists.test.tsx index ba7472f778..ca0dbd9513 100644 --- a/src/components/AccountLists/AccountLists.test.tsx +++ b/src/components/AccountLists/AccountLists.test.tsx @@ -73,7 +73,7 @@ describe('AccountLists', () => { , ); expect(getByRole('link')).toHaveTextContent( - 'AccountGoal$1,000*Gifts Started60%Committed80%*Last updated Jan 1, 2024', + 'AccountGoal$1,000*Gifts Started60%Committed80%*Below machine-calculated goal', ); }); @@ -211,4 +211,60 @@ describe('AccountLists', () => { expect(queryByText('Last updated Dec 30, 2019')).not.toBeInTheDocument(); }); }); + + describe('below machine-calculated warning', () => { + it('is shown if goal is less than the machine-calculated goal', () => { + const data = gqlMock(GetAccountListsDocument, { + mocks: { + accountLists: { + nodes: [ + { + currency: 'USD', + monthlyGoal: 5000, + healthIndicatorData: { + machineCalculatedGoal: 10000, + machineCalculatedGoalCurrency: 'USD', + }, + }, + ], + }, + }, + }); + + const { getByText } = render( + + + , + ); + expect(getByText('Below machine-calculated goal')).toBeInTheDocument(); + }); + + it('is hidden if goal is greater than or equal to the machine-calculated goal', async () => { + const data = gqlMock(GetAccountListsDocument, { + mocks: { + accountLists: { + nodes: [ + { + currency: 'USD', + monthlyGoal: 5000, + healthIndicatorData: { + machineCalculatedGoal: 5000, + machineCalculatedGoalCurrency: 'USD', + }, + }, + ], + }, + }, + }); + + const { queryByText } = render( + + + , + ); + expect( + queryByText('Below machine-calculated goal'), + ).not.toBeInTheDocument(); + }); + }); }); diff --git a/src/components/AccountLists/AccountLists.tsx b/src/components/AccountLists/AccountLists.tsx index 3e8575dc1f..1471dc674a 100644 --- a/src/components/AccountLists/AccountLists.tsx +++ b/src/components/AccountLists/AccountLists.tsx @@ -99,6 +99,7 @@ const AccountLists = ({ data }: Props): ReactElement => { goal, goalSource, preferencesGoalUpdatedAt, + preferencesGoalLow, preferencesGoalOld, } = getHealthIndicatorInfo(accountList, healthIndicatorData); @@ -108,22 +109,26 @@ const AccountLists = ({ data }: Props): ReactElement => { : NaN; const totalPercentage = hasValidGoal ? totalPledges / goal : NaN; - const annotation: Annotation | null = - goalSource === GoalSource.MachineCalculated - ? { - label: t('machine-calculated'), - color: 'statusWarning.main', - } - : preferencesGoalUpdatedAt - ? { - label: t('Last updated {{date}}', { - date: dateFormat(preferencesGoalUpdatedAt, locale), - }), - color: preferencesGoalOld - ? 'statusWarning.main' - : undefined, - } - : null; + const annotation: Annotation | null = preferencesGoalLow + ? { + label: t('Below machine-calculated goal'), + color: 'statusWarning.main', + } + : goalSource === GoalSource.MachineCalculated + ? { + label: t('machine-calculated'), + color: 'statusWarning.main', + } + : preferencesGoalUpdatedAt + ? { + label: t('Last updated {{date}}', { + date: dateFormat(preferencesGoalUpdatedAt, locale), + }), + color: preferencesGoalOld + ? 'statusWarning.main' + : undefined, + } + : null; const annotationId = `annotation-${id}`; return ( From b0fee782c470b61bbe5a053bb02158de30436b7a Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Wed, 26 Mar 2025 11:38:52 -0500 Subject: [PATCH 07/11] Adjust annotations based on new stakeholder mockups --- .../Dashboard/MonthlyGoal/MonthlyGoal.tsx | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx index c1f724a130..e196545142 100644 --- a/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx +++ b/src/components/Dashboard/MonthlyGoal/MonthlyGoal.tsx @@ -10,7 +10,6 @@ import { Theme, Tooltip, Typography, - TypographyProps, } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; @@ -56,7 +55,7 @@ const useStyles = makeStyles()((_theme: Theme) => ({ interface Annotation { label: string; - color: TypographyProps['color']; + warning: boolean; } export interface MonthlyGoalProps { @@ -144,19 +143,19 @@ const MonthlyGoal = ({ const annotation: Annotation | null = preferencesGoalLow ? { label: t('Below machine-calculated goal'), - color: 'statusWarning.main', + warning: true, } : goalSource === GoalSource.MachineCalculated ? { label: t('Machine-calculated goal'), - color: 'statusWarning.main', + warning: true, } : preferencesGoalUpdatedAt ? { label: t('Last updated {{date}}', { date: dateFormat(preferencesGoalUpdatedAt, locale), }), - color: preferencesGoalOld ? 'statusWarning.main' : 'textSecondary', + warning: preferencesGoalOld, } : null; const annotationId = useId(); @@ -238,7 +237,11 @@ const MonthlyGoal = ({ {annotation && ( * @@ -247,22 +250,12 @@ const MonthlyGoal = ({ )} - {annotation && ( - - * - {annotation.label} - - )} - {(goalSource === GoalSource.MachineCalculated || - preferencesGoalLow) && ( + {annotation?.warning && (