From ae0e672fcdeb967899716605d273ba06c1a927d8 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 09:41:57 -0700 Subject: [PATCH 01/13] Change PGA filter to use lowercase contact statuses --- pages/api/graphql-rest.page.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/pages/api/graphql-rest.page.ts b/pages/api/graphql-rest.page.ts index 95429d7be..a383dbca2 100644 --- a/pages/api/graphql-rest.page.ts +++ b/pages/api/graphql-rest.page.ts @@ -14,8 +14,6 @@ import { MergeContactsInput, MergePeopleBulkInput, } from 'src/graphql/types.generated'; -import i18n from 'src/lib/i18n'; -import { getLocalizedContactStatus } from '../../src/utils/functions/getLocalizedContactStatus'; import schema from './Schema'; import { getAccountListAnalytics } from './Schema/AccountListAnalytics/dataHandler'; import { getAccountListCoaches } from './Schema/AccountListCoaches/dataHandler'; @@ -615,16 +613,7 @@ class MpdxRestApi extends RESTDataSource { // Status case 'status': filters[snakedKey] = (value as ContactFilterStatusEnum[]) - .map((status) => { - const translated = getLocalizedContactStatus(i18n.t, status); - if (!translated) { - throw new Error( - `Unrecognized ContactFilterStatusEnum value ${value}`, - ); - } - - return translated; - }) + .map((status) => status.toLowerCase()) .join(','); break; From f34357d3b3c12f5f85df60ff983d4656a5099f46 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 10:44:20 -0700 Subject: [PATCH 02/13] Consolidate converting old Cru statuses --- .../reports/fourteenMonth/datahandler.ts | 57 +------------- .../Contacts/ContactFlow/useFlowOptions.ts | 73 +----------------- src/components/Shared/Filters/FilterPanel.tsx | 27 +++---- src/utils/functions/convertContactStatus.ts | 74 +++++++++++++++++++ 4 files changed, 89 insertions(+), 142 deletions(-) create mode 100644 src/utils/functions/convertContactStatus.ts diff --git a/pages/api/Schema/reports/fourteenMonth/datahandler.ts b/pages/api/Schema/reports/fourteenMonth/datahandler.ts index 43077632b..38f0968cc 100644 --- a/pages/api/Schema/reports/fourteenMonth/datahandler.ts +++ b/pages/api/Schema/reports/fourteenMonth/datahandler.ts @@ -1,7 +1,7 @@ +import { convertStatus } from 'src/utils/functions/convertContactStatus'; import { FourteenMonthReport, FourteenMonthReportCurrencyType, - StatusEnum, } from '../../../graphql-rest.page.generated'; export interface FourteenMonthReportResponse { @@ -69,61 +69,6 @@ export interface FourteenMonthReportResponse { }; } -// Convert a status string into a StatusEnum -const convertStatus = ( - status: string | null | undefined, -): StatusEnum | null => { - // Statuses will be lowercase and underscored (i.e. "never_contacted") after task phases lands - // Statuses will be sentence case with spaces (i.e. "Never Contacted") before task phases lands - switch (status) { - case 'never_contacted': - case 'Never Contacted': - return StatusEnum.NeverContacted; - case 'ask_in_future': - case 'Ask in Future': - return StatusEnum.AskInFuture; - case 'cultivate_relationship': - case 'Cultivate Relationship': - return StatusEnum.CultivateRelationship; - case 'contact_for_appointment': - case 'Contact for Appointment': - return StatusEnum.ContactForAppointment; - case 'appointment_scheduled': - case 'Appointment Scheduled': - return StatusEnum.AppointmentScheduled; - case 'call_for_decision': - case 'Call for Decision': - return StatusEnum.CallForDecision; - case 'partner_financial': - case 'Partner - Financial': - return StatusEnum.PartnerFinancial; - case 'partner_special': - case 'Partner - Special': - return StatusEnum.PartnerSpecial; - case 'partner_pray': - case 'Partner - Pray': - return StatusEnum.PartnerPray; - case 'not_interested': - case 'Not Interested': - return StatusEnum.NotInterested; - case 'unresponsive': - case 'Unresponsive': - return StatusEnum.Unresponsive; - case 'never_ask': - case 'Never Ask': - return StatusEnum.NeverAsk; - case 'research_abandoned': - case 'Research Abandoned': - return StatusEnum.ResearchAbandoned; - case 'expired_referral': - case 'Expired Referral': - return StatusEnum.ExpiredReferral; - - default: - return null; - } -}; - export const mapFourteenMonthReport = ( data: FourteenMonthReportResponse, currencyType: FourteenMonthReportCurrencyType, diff --git a/src/components/Contacts/ContactFlow/useFlowOptions.ts b/src/components/Contacts/ContactFlow/useFlowOptions.ts index c88c5dcc2..c302771bc 100644 --- a/src/components/Contacts/ContactFlow/useFlowOptions.ts +++ b/src/components/Contacts/ContactFlow/useFlowOptions.ts @@ -1,77 +1,8 @@ import { useMemo } from 'react'; import { StatusEnum } from 'src/graphql/types.generated'; +import { convertStatus } from 'src/utils/functions/convertContactStatus'; import { useGetUserOptionsQuery } from './GetUserOptions.generated'; -// Convert a status string from flow options into a StatusEnum -const convertFlowOptionStatus = (status: string): StatusEnum | null => { - // Flow options statuses are StatusEnum values (i.e. "NEVER_CONTACTED") after task phases - // Flow options statuses were in sentence case with spaces (i.e. "Never Contacted") before task phases - switch (status) { - case 'NEVER_CONTACTED': - case 'Never Contacted': - return StatusEnum.NeverContacted; - - case 'ASK_IN_FUTURE': - case 'Ask in Future': - return StatusEnum.AskInFuture; - - case 'CULTIVATE_RELATIONSHIP': - case 'Cultivate Relationship': - return StatusEnum.CultivateRelationship; - - case 'CONTACT_FOR_APPOINTMENT': - case 'Contact for Appointment': - return StatusEnum.ContactForAppointment; - - case 'APPOINTMENT_SCHEDULED': - case 'Appointment Scheduled': - return StatusEnum.AppointmentScheduled; - - case 'CALL_FOR_DECISION': - case 'Call for Decision': - return StatusEnum.CallForDecision; - - case 'PARTNER_FINANCIAL': - case 'Partner - Financial': - return StatusEnum.PartnerFinancial; - - case 'PARTNER_SPECIAL': - case 'Partner - Special': - return StatusEnum.PartnerSpecial; - - case 'PARTNER_PRAY': - case 'Partner - Pray': - return StatusEnum.PartnerPray; - - case 'NOT_INTERESTED': - case 'Not Interested': - return StatusEnum.NotInterested; - - case 'UNRESPONSIVE': - case 'UNRESPONSIVE': - return StatusEnum.Unresponsive; - - case 'NEVER_ASK': - case 'Never Ask': - return StatusEnum.NeverAsk; - - case 'RESEARCH_ABANDONED': - case 'Research Abandoned': - return StatusEnum.ResearchAbandoned; - - case 'RESEARCH_CONTACT_INFO': - case 'Research Contact Info': - return StatusEnum.ResearchContactInfo; - - case 'EXPIRED_REFERRAL': - case 'Expired Referral': - return StatusEnum.ExpiredReferral; - - default: - return null; - } -}; - const isTruthy = (value: T): value is NonNullable => Boolean(value); interface RawFlowOption extends Omit { @@ -100,7 +31,7 @@ export const useFlowOptions = (): UseFlowOptionReturn => { return rawOptions.map((option) => ({ ...option, statuses: option.statuses - .map((status) => convertFlowOptionStatus(status)) + .map((status) => convertStatus(status)) // Ignore null values that didn't match a valid status .filter(isTruthy), })); diff --git a/src/components/Shared/Filters/FilterPanel.tsx b/src/components/Shared/Filters/FilterPanel.tsx index 37d474bce..10055c89e 100644 --- a/src/components/Shared/Filters/FilterPanel.tsx +++ b/src/components/Shared/Filters/FilterPanel.tsx @@ -42,8 +42,8 @@ import { ResultEnum, TaskFilterSetInput, } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { sanitizeFilters } from 'src/lib/sanitizeFilters'; +import { convertStatus } from 'src/utils/functions/convertContactStatus'; import { ContactsContext, ContactsType, @@ -141,7 +141,6 @@ export const FilterPanel: React.FC = ({ }) => { const theme = useTheme(); const { t } = useTranslation(); - const { statusMapForFilters } = useContactPartnershipStatuses(); const activities = useApiConstants()?.activities; const [saveFilterModalOpen, setSaveFilterModalOpen] = useState(false); const [deleteFilterModalOpen, setDeleteFilterModalOpen] = useState(false); @@ -155,6 +154,14 @@ export const FilterPanel: React.FC = ({ FinancialAccountContext, ) as FinancialAccountType; + const matchFilterContactStatuses = (status: string | null | undefined) => { + return ( + Object.values(ContactFilterStatusEnum).find((value) => { + return String(value.toLowerCase()) === String(status?.toLowerCase()); + }) || null + ); + }; + const handleClearAll = contextType === ContextTypesEnum.Contacts ? contactsContext.handleClearAll @@ -446,20 +453,10 @@ export const FilterPanel: React.FC = ({ return { ...acc, [key]: value.split(',').map((enumValue) => { - // Status - // Check if saved filter (enumValue) is either the status (partner_financial) or (Partner - Financial) - const matchedStatus = Object.entries( - statusMapForFilters, - )?.find(([statusKey, status]) => { - return ( - statusKey === enumValue || - status === enumValue || - status.toLowerCase() === enumValue - ); - }); + // Saved Filters contact status could be in 'Partner - Financial' format from Angular or lowercase 'partner_financial' or uppercase ENUM return ( - (matchedStatus && - (matchedStatus[1] as ContactFilterStatusEnum)) || + convertStatus(enumValue) || + matchFilterContactStatuses(enumValue) || ContactFilterStatusEnum.Null ); }), diff --git a/src/utils/functions/convertContactStatus.ts b/src/utils/functions/convertContactStatus.ts new file mode 100644 index 000000000..76620711e --- /dev/null +++ b/src/utils/functions/convertContactStatus.ts @@ -0,0 +1,74 @@ +import { StatusEnum } from 'src/graphql/types.generated'; + +// Convert a status string into a StatusEnum +// Statuses may be lowercase and underscored (i.e. "never_contacted") after task phases lands +// Statuses may be sentence case with spaces (i.e. "Never Contacted") before task phases lands +export const convertStatus = ( + status: string | null | undefined, +): StatusEnum | null => { + const foundStatus = + Object.values(StatusEnum).find((value) => { + return String(value.toLowerCase()) === String(status?.toLowerCase()); + }) || null; + + if (foundStatus) { + return foundStatus; + } else { + return findOldStatus(status); + } +}; + +// Convert an old status string into a StatusEnum +export const findOldStatus = ( + status: string | null | undefined, +): StatusEnum | null => { + switch (status) { + case 'Never Contacted': + return StatusEnum.NeverContacted; + + case 'Ask in Future': + return StatusEnum.AskInFuture; + + case 'Cultivate Relationship': + return StatusEnum.CultivateRelationship; + + case 'Contact for Appointment': + return StatusEnum.ContactForAppointment; + + case 'Appointment Scheduled': + return StatusEnum.AppointmentScheduled; + + case 'Call for Decision': + return StatusEnum.CallForDecision; + + case 'Partner - Financial': + return StatusEnum.PartnerFinancial; + + case 'Partner - Special': + return StatusEnum.PartnerSpecial; + + case 'Partner - Pray': + return StatusEnum.PartnerPray; + + case 'Not Interested': + return StatusEnum.NotInterested; + + case 'UNRESPONSIVE': + return StatusEnum.Unresponsive; + + case 'Never Ask': + return StatusEnum.NeverAsk; + + case 'Research Abandoned': + return StatusEnum.ResearchAbandoned; + + case 'Research Contact Info': + return StatusEnum.ResearchContactInfo; + + case 'Expired Referral': + return StatusEnum.ExpiredReferral; + + default: + return null; + } +}; From 92f68c6698ead4593359ce645969e902c73390a0 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 10:50:31 -0700 Subject: [PATCH 03/13] Move getContactStatusesByPhase --- .../contacts/flows/setup.page.tsx | 5 ++- .../Contacts/ContactFlow/ContactFlow.tsx | 4 +- .../ResetToDefault/ResetToDefaultModal.tsx | 4 +- .../ContactFlow/contactFlowDefaultOptions.ts | 38 ++++--------------- .../SuggestedContactStatus.tsx | 12 +++--- src/hooks/useContactPartnershipStatuses.ts | 12 +++++- src/utils/functions/getLocalizedPhase.ts | 14 +------ 7 files changed, 32 insertions(+), 57 deletions(-) diff --git a/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx b/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx index 3fa5baef8..1b4906cb4 100644 --- a/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx +++ b/pages/accountLists/[accountListId]/contacts/flows/setup.page.tsx @@ -45,7 +45,8 @@ const StickyBox = styled(Box)(() => ({ const ContactFlowSetupPage: React.FC = () => { const { t } = useTranslation(); const accountListId = useAccountListId(); - const { statusMap, contactStatuses } = useContactPartnershipStatuses(); + const { statusMap, getContactStatusesByPhase } = + useContactPartnershipStatuses(); const { enqueueSnackbar } = useSnackbar(); const [flowOptions, setFlowOptions] = useState([]); const resetColumnsMessage = t( @@ -55,7 +56,7 @@ const ContactFlowSetupPage: React.FC = () => { useEffect(() => { if (!userOptions.length) { - setFlowOptions(getDefaultFlowOptions(t, contactStatuses)); + setFlowOptions(getDefaultFlowOptions(t, getContactStatusesByPhase)); } else { setFlowOptions(userOptions); } diff --git a/src/components/Contacts/ContactFlow/ContactFlow.tsx b/src/components/Contacts/ContactFlow/ContactFlow.tsx index e94ee25de..de7f66100 100644 --- a/src/components/Contacts/ContactFlow/ContactFlow.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlow.tsx @@ -56,9 +56,9 @@ export const ContactFlow: React.FC = ({ useFlowOptions(); const { t } = useTranslation(); + const { getContactStatusesByPhase } = useContactPartnershipStatuses(); const { enqueueSnackbar } = useSnackbar(); const { openTaskModal } = useTaskModal(); - const { contactStatuses } = useContactPartnershipStatuses(); const flowOptions = useMemo(() => { if (loadingUserOptions) { @@ -68,7 +68,7 @@ export const ContactFlow: React.FC = ({ return userFlowOptions; } - return getDefaultFlowOptions(t, contactStatuses); + return getDefaultFlowOptions(t, getContactStatusesByPhase); }, [userFlowOptions, loadingUserOptions]); const [updateContactOther] = useUpdateContactOtherMutation(); diff --git a/src/components/Contacts/ContactFlow/ContactFlowSetup/Header/ResetToDefault/ResetToDefaultModal.tsx b/src/components/Contacts/ContactFlow/ContactFlowSetup/Header/ResetToDefault/ResetToDefaultModal.tsx index 3c6f0b523..bfa062ff9 100644 --- a/src/components/Contacts/ContactFlow/ContactFlowSetup/Header/ResetToDefault/ResetToDefaultModal.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlowSetup/Header/ResetToDefault/ResetToDefaultModal.tsx @@ -41,14 +41,14 @@ export const ResetToDefaultModal: React.FC = ({ resetColumnsMessage, }) => { const { t } = useTranslation(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getContactStatusesByPhase } = useContactPartnershipStatuses(); const { enqueueSnackbar } = useSnackbar(); const [updating, setUpdating] = useState(false); const handleOnSubmit = async (values: { resetToDefaultType: string }) => { const defaultValues = getDefaultFlowOptions( t, - contactStatuses, + getContactStatusesByPhase, values.resetToDefaultType as DefaultTypeEnum, ); diff --git a/src/components/Contacts/ContactFlow/contactFlowDefaultOptions.ts b/src/components/Contacts/ContactFlow/contactFlowDefaultOptions.ts index 8a1686b61..b3814db7a 100644 --- a/src/components/Contacts/ContactFlow/contactFlowDefaultOptions.ts +++ b/src/components/Contacts/ContactFlow/contactFlowDefaultOptions.ts @@ -1,11 +1,7 @@ import { TFunction } from 'i18next'; import { v4 as uuidv4 } from 'uuid'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; -import { ContactStatuses } from 'src/hooks/useContactPartnershipStatuses'; -import { - getContactStatusesByPhase, - getLocalizedPhase, -} from 'src/utils/functions/getLocalizedPhase'; +import { getLocalizedPhase } from 'src/utils/functions/getLocalizedPhase'; import { ContactFlowOption } from './ContactFlow'; export enum DefaultTypeEnum { @@ -15,7 +11,7 @@ export enum DefaultTypeEnum { export const getDefaultFlowOptions = ( t: TFunction, - contactStatuses: ContactStatuses, + getContactStatusesByPhase: (phase: string | null) => StatusEnum[], type: DefaultTypeEnum = DefaultTypeEnum.Us, ): ContactFlowOption[] => { switch (type) { @@ -66,55 +62,37 @@ export const getDefaultFlowOptions = ( { id: uuidv4(), name: getLocalizedPhase(t, PhaseEnum.Connection), - statuses: getContactStatusesByPhase( - PhaseEnum.Connection, - contactStatuses, - ).map((status) => status.id), + statuses: getContactStatusesByPhase(PhaseEnum.Connection), color: 'color-warning', }, { id: uuidv4(), name: getLocalizedPhase(t, PhaseEnum.Initiation), - statuses: getContactStatusesByPhase( - PhaseEnum.Initiation, - contactStatuses, - ).map((status) => status.id), + statuses: getContactStatusesByPhase(PhaseEnum.Initiation), color: 'color-info', }, { id: uuidv4(), name: getLocalizedPhase(t, PhaseEnum.Appointment), - statuses: getContactStatusesByPhase( - PhaseEnum.Appointment, - contactStatuses, - ).map((status) => status.id), + statuses: getContactStatusesByPhase(PhaseEnum.Appointment), color: 'color-success', }, { id: uuidv4(), name: getLocalizedPhase(t, PhaseEnum.FollowUp), - statuses: getContactStatusesByPhase( - PhaseEnum.FollowUp, - contactStatuses, - ).map((status) => status.id), + statuses: getContactStatusesByPhase(PhaseEnum.FollowUp), color: 'color-warning', }, { id: uuidv4(), name: getLocalizedPhase(t, PhaseEnum.PartnerCare), - statuses: getContactStatusesByPhase( - PhaseEnum.PartnerCare, - contactStatuses, - ).map((status) => status.id), + statuses: getContactStatusesByPhase(PhaseEnum.PartnerCare), color: 'color-success', }, { id: uuidv4(), name: getLocalizedPhase(t, PhaseEnum.Archive), - statuses: getContactStatusesByPhase( - PhaseEnum.Archive, - contactStatuses, - ).map((status) => status.id), + statuses: getContactStatusesByPhase(PhaseEnum.Archive), color: 'color-text', }, ]; diff --git a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx index 59d9271cc..ffb331b3b 100644 --- a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx +++ b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx @@ -3,7 +3,7 @@ import { Checkbox, FormControl, FormControlLabel, Grid } from '@mui/material'; import { Trans } from 'react-i18next'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; -import { getContactStatusesByPhase } from 'src/utils/functions/getLocalizedPhase'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { useContactStatusQuery } from './SuggestedContactStatus.generated'; export type FormikHandleChange = { @@ -44,7 +44,8 @@ export const SuggestedContactStatus: React.FC = ({ skip: !!contactStatus, }); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); + const { getContactStatusesByPhase } = useContactPartnershipStatuses(); const currentContactStatus: StatusEnum | null | undefined = contactStatus || data?.contact.status; @@ -58,14 +59,13 @@ export const SuggestedContactStatus: React.FC = ({ if (currentContactStatus) { const disabledStatuses: StatusEnum[] = getContactStatusesByPhase( PhaseEnum.PartnerCare, - contactStatuses, - ).map((s) => s.id); + ); // Hide suggestedStatus if the suggested status is a partner care status, otherwise, show it return !disabledStatuses.includes(currentContactStatus); } // Show suggestedStatus if the contact has no status return true; - }, [contactStatuses, currentContactStatus]); + }, [currentContactStatus]); return suggestedContactStatus && shouldRenderContactSuggestion ? ( @@ -82,7 +82,7 @@ export const SuggestedContactStatus: React.FC = ({ }} /> diff --git a/src/hooks/useContactPartnershipStatuses.ts b/src/hooks/useContactPartnershipStatuses.ts index e35f674f0..ef2d10de8 100644 --- a/src/hooks/useContactPartnershipStatuses.ts +++ b/src/hooks/useContactPartnershipStatuses.ts @@ -2,7 +2,6 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; interface ContactStatus { name: string; @@ -61,7 +60,7 @@ export const useContactPartnershipStatuses = () => { )?.value; acc[status] = { name: statusName, - translated: getLocalizedContactStatus(t, status), + translated: statusName, phase: phase.id, }; }); @@ -112,10 +111,19 @@ export const useContactPartnershipStatuses = () => { return { id: statusKey, ...s }; }); + const getContactStatusesByPhase = ( + phase: PhaseEnum | string | null, + ): StatusEnum[] => { + return ( + phases?.find((p) => String(p.id) === String(phase))?.contactStatuses || [] + ); + }; + return { contactStatuses, statusMap, statusMapForFilters, statusArray, + getContactStatusesByPhase, }; }; diff --git a/src/utils/functions/getLocalizedPhase.ts b/src/utils/functions/getLocalizedPhase.ts index af48f58ec..44914f653 100644 --- a/src/utils/functions/getLocalizedPhase.ts +++ b/src/utils/functions/getLocalizedPhase.ts @@ -1,6 +1,5 @@ import { TFunction } from 'react-i18next'; -import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; -import { ContactStatuses } from 'src/hooks/useContactPartnershipStatuses'; +import { PhaseEnum } from 'src/graphql/types.generated'; export const getLocalizedPhase = ( t: TFunction, @@ -33,14 +32,3 @@ export const getLocalizedPhase = ( return ''; } }; - -export const getContactStatusesByPhase = ( - phase: PhaseEnum | null, - contactStatuses: ContactStatuses, -): { id: StatusEnum; translated: string }[] => { - return Object.entries(contactStatuses) - .filter(([_, status]) => status.phase === phase) - .map(([id, status]) => { - return { id: id as StatusEnum, translated: status.translated }; - }); -}; From 1b5a02d4fbf71a6528891cdd3a73e76bb3097c0f Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 10:53:42 -0700 Subject: [PATCH 04/13] Add useLocalizedContacts() hook and replace getLocalizedStatus --- .../CoachingDetail/Activity/Activity.tsx | 16 +++---- .../ContactHeaderStatusSection.tsx | 6 +-- .../EditPartnershipInfoModal.tsx | 6 +-- .../ContactFlowRowPreview.tsx | 7 ++- .../ContactFlowDropZone.tsx | 7 ++- .../ContactFlowRow/ContactFlowRow.tsx | 7 ++- .../ContactFlowSetupDragPreviewStatus.tsx | 7 ++- .../Row/ContactFlowSetupStatusRow.tsx | 7 ++- .../ContactPartnershipStatusLabel.tsx | 6 +-- .../Contacts/ContactsMap/ContactsMapPanel.tsx | 2 +- .../EditFields/MassActionsEditFieldsModal.tsx | 6 +-- .../Merge/MassActionsMergeModal.tsx | 6 +-- .../CreateMultipleContacts.tsx | 6 +-- .../TopBar/Items/SearchMenu/SearchDialog.tsx | 7 ++- .../Table/ExpectedMonthlyTotalReportTable.tsx | 9 ++-- .../Layout/Table/Table.tsx | 7 ++- .../FourteenMonthReports/useCsvData.ts | 8 ++-- .../ContactFlowRowPreview.tsx | 7 ++- .../Flow/ContactFlowRow/ContactFlowRow.tsx | 7 ++- .../Tool/FixCommitmentInfo/Contact.tsx | 12 ++--- .../Tool/FixMailingAddresses/Contact.tsx | 8 ++-- .../Tool/FixSendNewsletter/Contact.tsx | 5 +- .../Tool/MergeContacts/ContactPair.tsx | 6 +-- src/hooks/useLocalizedConstants.ts | 37 +++++++++++++++ .../functions/getLocalizedContactStatus.ts | 46 ------------------- 25 files changed, 110 insertions(+), 138 deletions(-) create mode 100644 src/hooks/useLocalizedConstants.ts delete mode 100644 src/utils/functions/getLocalizedContactStatus.ts diff --git a/src/components/Coaching/CoachingDetail/Activity/Activity.tsx b/src/components/Coaching/CoachingDetail/Activity/Activity.tsx index b65fe410d..add4d8631 100644 --- a/src/components/Coaching/CoachingDetail/Activity/Activity.tsx +++ b/src/components/Coaching/CoachingDetail/Activity/Activity.tsx @@ -24,12 +24,12 @@ import { StatusEnum, } from 'src/graphql/types.generated'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { currencyFormat, dateFormat, dateFormatWithoutYear, } from 'src/lib/intlFormat'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; import { MultilineSkeleton } from '../../../Shared/MultilineSkeleton'; import { AccountListTypeEnum, CoachingPeriodEnum } from '../CoachingDetail'; import { CoachingLink } from '../CoachingLink'; @@ -177,6 +177,7 @@ export const Activity: React.FC = ({ primaryAppeal, }) => { const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const locale = useLocale(); const periodDuration = @@ -288,7 +289,7 @@ export const Activity: React.FC = ({ {data?.accountListAnalytics.contactsByStatus.neverContacted} - {getLocalizedContactStatus(t, StatusEnum.NeverContacted)} + {getLocalizedContactStatus(StatusEnum.NeverContacted)} @@ -303,7 +304,7 @@ export const Activity: React.FC = ({ {data?.accountListAnalytics.contactsByStatus.future} - {getLocalizedContactStatus(t, StatusEnum.AskInFuture)} + {getLocalizedContactStatus(StatusEnum.AskInFuture)} @@ -319,7 +320,6 @@ export const Activity: React.FC = ({ {getLocalizedContactStatus( - t, StatusEnum.CultivateRelationship, )} @@ -366,7 +366,6 @@ export const Activity: React.FC = ({ {getLocalizedContactStatus( - t, StatusEnum.ContactForAppointment, )} @@ -386,10 +385,7 @@ export const Activity: React.FC = ({ } - {getLocalizedContactStatus( - t, - StatusEnum.AppointmentScheduled, - )} + {getLocalizedContactStatus(StatusEnum.AppointmentScheduled)} @@ -407,7 +403,7 @@ export const Activity: React.FC = ({ } - {getLocalizedContactStatus(t, StatusEnum.CallForDecision)} + {getLocalizedContactStatus(StatusEnum.CallForDecision)} diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.tsx index 1e93f854d..ca7332f2b 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.tsx @@ -4,8 +4,8 @@ import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { StatusEnum } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { getLocalizedPledgeFrequency } from 'src/utils/functions/getLocalizedPledgeFrequency'; import { currencyFormat } from '../../../../../lib/intlFormat'; import { @@ -35,7 +35,7 @@ export const ContactHeaderStatusSection: React.FC = ({ }) => { const { t } = useTranslation(); const locale = useLocale(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const status = contact?.status; const [editPartnershipModalOpen, setEditPartnershipModalOpen] = useState(false); @@ -65,7 +65,7 @@ export const ContactHeaderStatusSection: React.FC = ({ ); } else { - const statusText = status && contactStatuses[status]?.translated; + const statusText = getLocalizedContactStatus(status); return ( <> {status && ( diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx index ee2d76b2f..fe35bdf71 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.tsx @@ -36,8 +36,8 @@ import { SendNewsletterEnum, StatusEnum, } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { nullableDateTime } from 'src/lib/formikHelpers'; import { getPledgeCurrencyOptions } from 'src/lib/getCurrencyOptions'; import { getLocalizedLikelyToGive } from 'src/utils/functions/getLocalizedLikelyToGive'; @@ -123,7 +123,7 @@ export const EditPartnershipInfoModal: React.FC< const { appName } = useGetAppSettings(); const accountListId = useAccountListId(); const constants = useApiConstants(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const phases = constants?.phases; const [showRemoveCommitmentWarning, setShowRemoveCommitmentWarning] = @@ -319,7 +319,7 @@ export const EditPartnershipInfoModal: React.FC< , phase?.contactStatuses.map((status) => ( - {contactStatuses[status]?.translated} + {getLocalizedContactStatus(status)} )), ])} diff --git a/src/components/Contacts/ContactFlow/ContactFlowDragLayer/ContactFlowRowPreview.tsx b/src/components/Contacts/ContactFlow/ContactFlowDragLayer/ContactFlowRowPreview.tsx index c439dcf3f..e5df7d3c8 100644 --- a/src/components/Contacts/ContactFlow/ContactFlowDragLayer/ContactFlowRowPreview.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlowDragLayer/ContactFlowRowPreview.tsx @@ -3,9 +3,8 @@ import Star from '@mui/icons-material/Star'; import StarBorder from '@mui/icons-material/StarBorder'; import { Avatar, Box, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; -import { useTranslation } from 'react-i18next'; import { StatusEnum } from 'src/graphql/types.generated'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from '../../../../theme'; export const PreviewBox = styled(Box)(({ theme }) => ({ @@ -36,7 +35,7 @@ export interface ContactFlowRowPreviewProps { export const ContactFlowRowPreview: React.FC = memo( function ContactFlowRowPreview({ name, status, starred, width }) { - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); return ( @@ -53,7 +52,7 @@ export const ContactFlowRowPreview: React.FC = memo( {name} - {getLocalizedContactStatus(t, status)} + {getLocalizedContactStatus(status)} diff --git a/src/components/Contacts/ContactFlow/ContactFlowDropZone/ContactFlowDropZone.tsx b/src/components/Contacts/ContactFlow/ContactFlowDropZone/ContactFlowDropZone.tsx index 3ef3ac165..e06e8cefb 100644 --- a/src/components/Contacts/ContactFlow/ContactFlowDropZone/ContactFlowDropZone.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlowDropZone/ContactFlowDropZone.tsx @@ -2,9 +2,8 @@ import React from 'react'; import { Box, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useDrop } from 'react-dnd'; -import { useTranslation } from 'react-i18next'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from '../../../../theme'; import { DraggedContact } from '../ContactFlowRow/ContactFlowRow'; @@ -44,7 +43,7 @@ export const ContactFlowDropZone: React.FC = ({ status, changeContactStatus, }: Props) => { - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const [{ isOver, canDrop }, drop] = useDrop(() => ({ accept: 'contact', canDrop: (contact) => contact.status !== status, @@ -60,7 +59,7 @@ export const ContactFlowDropZone: React.FC = ({ return ( - {getLocalizedContactStatus(t, status)} + {getLocalizedContactStatus(status)} ); diff --git a/src/components/Contacts/ContactFlow/ContactFlowRow/ContactFlowRow.tsx b/src/components/Contacts/ContactFlow/ContactFlowRow/ContactFlowRow.tsx index e0a9585de..df56a7b12 100644 --- a/src/components/Contacts/ContactFlow/ContactFlowRow/ContactFlowRow.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlowRow/ContactFlowRow.tsx @@ -3,9 +3,8 @@ import { Avatar, Box, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useDrag } from 'react-dnd'; import { getEmptyImage } from 'react-dnd-html5-backend'; -import { useTranslation } from 'react-i18next'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from '../../../../theme'; import { ContactRowFragment } from '../../ContactRow/ContactRow.generated'; import { StarContactIconButton } from '../../StarContactIconButton/StarContactIconButton'; @@ -81,7 +80,7 @@ export const ContactFlowRow: React.FC = ({ }) => { const { id, name, starred, avatar } = contact; - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const item: DraggedContact = { id, @@ -115,7 +114,7 @@ export const ContactFlowRow: React.FC = ({ onContactSelected(id, true, true)}> {name} - {getLocalizedContactStatus(t, status)} + {getLocalizedContactStatus(status)} diff --git a/src/components/Contacts/ContactFlow/ContactFlowSetup/DragPreview/ContactFlowSetupDragPreviewStatus.tsx b/src/components/Contacts/ContactFlow/ContactFlowSetup/DragPreview/ContactFlowSetupDragPreviewStatus.tsx index 63e805be3..2901b9f3f 100644 --- a/src/components/Contacts/ContactFlow/ContactFlowSetup/DragPreview/ContactFlowSetupDragPreviewStatus.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlowSetup/DragPreview/ContactFlowSetupDragPreviewStatus.tsx @@ -1,10 +1,9 @@ import React, { memo } from 'react'; import { Box, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; -import { useTranslation } from 'react-i18next'; import { StatusEnum } from 'src/graphql/types.generated'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from 'src/theme'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; interface Props { status: StatusEnum; @@ -23,11 +22,11 @@ const DragLayerStatusBox = styled(Box, { export const ContactFlowSetupDragPreviewStatus: React.FC = memo( function ContactFlowSetupDragPreviewStatus({ status, width }) { - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); return ( - {getLocalizedContactStatus(t, status)} + {getLocalizedContactStatus(status)} ); }, diff --git a/src/components/Contacts/ContactFlow/ContactFlowSetup/Row/ContactFlowSetupStatusRow.tsx b/src/components/Contacts/ContactFlow/ContactFlowSetup/Row/ContactFlowSetupStatusRow.tsx index 10f59c9f5..e1fc69659 100644 --- a/src/components/Contacts/ContactFlow/ContactFlowSetup/Row/ContactFlowSetupStatusRow.tsx +++ b/src/components/Contacts/ContactFlow/ContactFlowSetup/Row/ContactFlowSetupStatusRow.tsx @@ -3,9 +3,8 @@ import { Box, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import { useDrag } from 'react-dnd'; import { getEmptyImage } from 'react-dnd-html5-backend'; -import { useTranslation } from 'react-i18next'; import { StatusEnum } from 'src/graphql/types.generated'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from '../../../../../theme'; const StatusRow = styled(Box)(() => ({ @@ -35,7 +34,7 @@ export const ContactFlowSetupStatusRow: React.FC = ({ columnWidth, columnIndex, }: Props) => { - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const item: ContactFlowSetupItemDrag = { status, columnWidth, @@ -55,7 +54,7 @@ export const ContactFlowSetupStatusRow: React.FC = ({ return ( - {getLocalizedContactStatus(t, status)} + {getLocalizedContactStatus(status)} ); }; diff --git a/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatusLabel/ContactPartnershipStatusLabel.tsx b/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatusLabel/ContactPartnershipStatusLabel.tsx index 33389a9a4..56dcd8999 100644 --- a/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatusLabel/ContactPartnershipStatusLabel.tsx +++ b/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatusLabel/ContactPartnershipStatusLabel.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Typography } from '@mui/material'; import { StatusEnum as ContactPartnershipStatusEnum } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; interface ContactPartnershipStatusLabelProps { status: ContactPartnershipStatusEnum; @@ -10,6 +10,6 @@ interface ContactPartnershipStatusLabelProps { export const ContactPartnershipStatusLabel: React.FC< ContactPartnershipStatusLabelProps > = ({ status }) => { - const { contactStatuses } = useContactPartnershipStatuses(); - return {contactStatuses[status]?.translated}; + const { getLocalizedContactStatus } = useLocalizedConstants(); + return {getLocalizedContactStatus(status)}; }; diff --git a/src/components/Contacts/ContactsMap/ContactsMapPanel.tsx b/src/components/Contacts/ContactsMap/ContactsMapPanel.tsx index 6640282b1..c8f862d2e 100644 --- a/src/components/Contacts/ContactsMap/ContactsMapPanel.tsx +++ b/src/components/Contacts/ContactsMap/ContactsMapPanel.tsx @@ -118,7 +118,7 @@ export const ContactsMapPanel: React.FC = ({ .map(([statusKey, status]) => { return { key: statusKey, - title: status.translated, + title: status.name, imgUrl: `/images/pin_${statusKey.toLowerCase()}.png`, data: data?.filter( (contact) => contact?.lat && contact?.status === statusKey, diff --git a/src/components/Contacts/MassActions/EditFields/MassActionsEditFieldsModal.tsx b/src/components/Contacts/MassActions/EditFields/MassActionsEditFieldsModal.tsx index 3d346b880..6ff6de29e 100644 --- a/src/components/Contacts/MassActions/EditFields/MassActionsEditFieldsModal.tsx +++ b/src/components/Contacts/MassActions/EditFields/MassActionsEditFieldsModal.tsx @@ -27,7 +27,7 @@ import { LikelyToGiveEnum, SendNewsletterEnum, } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { getPledgeCurrencyOptions } from 'src/lib/getCurrencyOptions'; import { getLocalizedLikelyToGive } from 'src/utils/functions/getLocalizedLikelyToGive'; import { getLocalizedPhase } from 'src/utils/functions/getLocalizedPhase'; @@ -115,7 +115,7 @@ export const MassActionsEditFieldsModal: React.FC< useLoadConstantsQuery(); const phases = useApiConstants()?.phases; - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); return ( @@ -181,7 +181,7 @@ export const MassActionsEditFieldsModal: React.FC< , phase?.contactStatuses.map((status) => ( - {contactStatuses[status]?.translated} + {getLocalizedContactStatus(status)} )), ])} diff --git a/src/components/Contacts/MassActions/Merge/MassActionsMergeModal.tsx b/src/components/Contacts/MassActions/Merge/MassActionsMergeModal.tsx index a9479c31d..2bc8e4f30 100644 --- a/src/components/Contacts/MassActions/Merge/MassActionsMergeModal.tsx +++ b/src/components/Contacts/MassActions/Merge/MassActionsMergeModal.tsx @@ -17,9 +17,9 @@ import { SubmitButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { dateFormatShort } from 'src/lib/intlFormat'; import theme from 'src/theme'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; import { sourceToStr } from 'src/utils/sourceHelper'; import Modal from '../../../common/Modal/Modal'; import { @@ -50,6 +50,7 @@ export const MassActionsMergeModal: React.FC = ({ const { t } = useTranslation(); const locale = useLocale(); const { enqueueSnackbar } = useSnackbar(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const [primaryContactId, setPrimaryContactId] = useState(ids[0]); @@ -155,8 +156,7 @@ export const MassActionsMergeModal: React.FC = ({ {contact.name} - {t('Status')}:{' '} - {getLocalizedContactStatus(t, contact.status)} + {t('Status')}: {getLocalizedContactStatus(contact.status)}
{contact.primaryAddress && ( <> diff --git a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx index f1da80177..ce70bcd91 100644 --- a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx +++ b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx @@ -35,7 +35,7 @@ import { SubmitButton, } from 'src/components/common/Modal/ActionButtons/ActionButtons'; import { PersonCreateInput, StatusEnum } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { getLocalizedPhase } from 'src/utils/functions/getLocalizedPhase'; import theme from '../../../../../../../../theme'; import { useCreateContactMutation } from '../CreateContact/CreateContact.generated'; @@ -138,7 +138,7 @@ export const CreateMultipleContacts = ({ }; const constants = useApiConstants(); const phases = constants?.phases; - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const [createContact] = useCreateContactMutation(); const [createPerson] = useCreatePersonMutation(); const [createAddress] = useCreateContactAddressMutation(); @@ -462,7 +462,7 @@ export const CreateMultipleContacts = ({ , phase?.contactStatuses.map((status) => ( - {contactStatuses[status]?.translated} + {getLocalizedContactStatus(status)} )), ])} diff --git a/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchDialog.tsx b/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchDialog.tsx index 094f7957c..93e85681e 100644 --- a/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchDialog.tsx +++ b/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchDialog.tsx @@ -23,7 +23,7 @@ import { ContactFilterStatusEnum, StatusEnum, } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { useAccountListId } from '../../../../../../hooks/useAccountListId'; import { useCreateContactMutation } from '../AddMenu/Items/CreateContact/CreateContact.generated'; import { useGetSearchMenuContactsLazyQuery } from './SearchMenu.generated'; @@ -64,7 +64,7 @@ export const SearchDialog: React.FC = ({ handleClose }) => { const accountListId = useAccountListId(); const { enqueueSnackbar } = useSnackbar(); const { push } = useRouter(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); //#region Search const [wildcardSearch, setWildcardSearch] = useState(''); @@ -306,8 +306,7 @@ export const SearchDialog: React.FC = ({ handleClose }) => { {option.name} - {option.status && - contactStatuses[option.status].translated} + {getLocalizedContactStatus(option.status)} diff --git a/src/components/Reports/ExpectedMonthlyTotalReport/Table/ExpectedMonthlyTotalReportTable.tsx b/src/components/Reports/ExpectedMonthlyTotalReport/Table/ExpectedMonthlyTotalReportTable.tsx index ea094a2b3..d0c72e466 100644 --- a/src/components/Reports/ExpectedMonthlyTotalReport/Table/ExpectedMonthlyTotalReportTable.tsx +++ b/src/components/Reports/ExpectedMonthlyTotalReport/Table/ExpectedMonthlyTotalReportTable.tsx @@ -17,8 +17,8 @@ import { } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { ExpectedDonationRowFragment } from 'pages/accountLists/[accountListId]/reports/GetExpectedMonthlyTotals.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { currencyFormat } from 'src/lib/intlFormat'; import theme from '../../../../theme'; @@ -41,7 +41,7 @@ export const ExpectedMonthlyTotalReportTable: React.FC = ({ }) => { const { t } = useTranslation(); const locale = useLocale(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const [visible, setVisible] = useState(true); @@ -121,8 +121,9 @@ export const ExpectedMonthlyTotalReportTable: React.FC = ({ {row.contactStatus && - contactStatuses[row.contactStatus.toUpperCase()] - ?.translated} + getLocalizedContactStatus( + row.contactStatus.toUpperCase(), + )} {currencyFormat( diff --git a/src/components/Reports/FourteenMonthReports/Layout/Table/Table.tsx b/src/components/Reports/FourteenMonthReports/Layout/Table/Table.tsx index aec93c5d9..586d7192c 100644 --- a/src/components/Reports/FourteenMonthReports/Layout/Table/Table.tsx +++ b/src/components/Reports/FourteenMonthReports/Layout/Table/Table.tsx @@ -12,8 +12,8 @@ import { import { styled } from '@mui/material/styles'; import { useTranslation } from 'react-i18next'; import { preloadContactsRightPanel } from 'src/components/Contacts/ContactsRightPanel/DynamicContactsRightPanel'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from 'src/theme'; import { numberFormat } from '../../../../../lib/intlFormat'; import { useApiConstants } from '../../../../Constants/UseApiConstants'; @@ -79,7 +79,7 @@ export const FourteenMonthReportTable: React.FC< const { t } = useTranslation(); const locale = useLocale(); const apiConstants = useApiConstants(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const totalAverage = useMemo( () => @@ -148,8 +148,7 @@ export const FourteenMonthReportTable: React.FC< {isExpanded && ( <> - {contact.status && - contactStatuses[contact.status.toUpperCase()]?.translated} + {getLocalizedContactStatus(contact?.status?.toUpperCase())} {contact.pledgeAmount && diff --git a/src/components/Reports/FourteenMonthReports/useCsvData.ts b/src/components/Reports/FourteenMonthReports/useCsvData.ts index 5f0716064..cf0968609 100644 --- a/src/components/Reports/FourteenMonthReports/useCsvData.ts +++ b/src/components/Reports/FourteenMonthReports/useCsvData.ts @@ -3,8 +3,8 @@ import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { PledgeFrequencyEnum, StatusEnum } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { getLocalizedPledgeFrequency } from 'src/utils/functions/getLocalizedPledgeFrequency'; import type { CurrencyTable } from './FourteenMonthReport'; @@ -23,7 +23,7 @@ export const useCsvData = (currencyTables: CurrencyTable[]): CsvData => { const { t } = useTranslation(); const locale = useLocale(); const apiConstants = useApiConstants(); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const csvData = useMemo( () => @@ -98,9 +98,7 @@ export const useCsvData = (currencyTables: CurrencyTable[]): CsvData => { return [ contact.name, - contact.status - ? contactStatuses[contact.status.toUpperCase()]?.translated - : '', + getLocalizedContactStatus(contact.status?.toUpperCase()), contact.pledgeAmount ? Math.round(contact.pledgeAmount) : 0, contact.pledgeCurrency ?? '', getLocalizedPledgeFrequency(t, pledgeFrequency), diff --git a/src/components/Tool/Appeal/Flow/ContactFlowDragLayer/ContactFlowRowPreview.tsx b/src/components/Tool/Appeal/Flow/ContactFlowDragLayer/ContactFlowRowPreview.tsx index 89350c031..d9bf2b5ff 100644 --- a/src/components/Tool/Appeal/Flow/ContactFlowDragLayer/ContactFlowRowPreview.tsx +++ b/src/components/Tool/Appeal/Flow/ContactFlowDragLayer/ContactFlowRowPreview.tsx @@ -2,7 +2,6 @@ import React, { memo } from 'react'; import Star from '@mui/icons-material/Star'; import StarBorder from '@mui/icons-material/StarBorder'; import { Avatar, Box, Typography } from '@mui/material'; -import { useTranslation } from 'react-i18next'; import { ContactFlowRowPreviewProps, DetailsBox, @@ -10,8 +9,8 @@ import { PreviewInnerBox, } from 'src/components/Contacts/ContactFlow/ContactFlowDragLayer/ContactFlowRowPreview'; import { StatusEnum } from 'src/graphql/types.generated'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from 'src/theme'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; interface Props extends Omit { status: StatusEnum; @@ -19,7 +18,7 @@ interface Props extends Omit { export const ContactFlowRowPreview: React.FC = memo( function ContactFlowRowPreview({ name, status, starred, width }) { - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); return ( @@ -35,7 +34,7 @@ export const ContactFlowRowPreview: React.FC = memo( {name} - {getLocalizedContactStatus(t, status)} + {getLocalizedContactStatus(status)}
diff --git a/src/components/Tool/Appeal/Flow/ContactFlowRow/ContactFlowRow.tsx b/src/components/Tool/Appeal/Flow/ContactFlowRow/ContactFlowRow.tsx index 50622756e..16c02718b 100644 --- a/src/components/Tool/Appeal/Flow/ContactFlowRow/ContactFlowRow.tsx +++ b/src/components/Tool/Appeal/Flow/ContactFlowRow/ContactFlowRow.tsx @@ -11,7 +11,6 @@ import { import { styled } from '@mui/material/styles'; import { useDrag } from 'react-dnd'; import { getEmptyImage } from 'react-dnd-html5-backend'; -import { useTranslation } from 'react-i18next'; import { ContactFlowRowProps, ContactLink, @@ -22,8 +21,8 @@ import { import { StarContactIconButton } from 'src/components/Contacts/StarContactIconButton/StarContactIconButton'; import { useGetPledgeOrDonation } from 'src/components/Tool/Appeal/Shared/useGetPledgeOrDonation/useGetPledgeOrDonation'; import { StatusEnum } from 'src/graphql/types.generated'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import theme from 'src/theme'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; import { AppealStatusEnum, AppealsContext, @@ -96,7 +95,7 @@ export const ContactFlowRow: React.FC = ({ excludedContacts, }) => { const { id, name, starred } = contact; - const { t } = useTranslation(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const { appealId, isRowChecked: isChecked, @@ -175,7 +174,7 @@ export const ContactFlowRow: React.FC = ({ {name} - {getLocalizedContactStatus(t, contact.status)} + {getLocalizedContactStatus(contact.status)} diff --git a/src/components/Tool/FixCommitmentInfo/Contact.tsx b/src/components/Tool/FixCommitmentInfo/Contact.tsx index 1df3c40de..5efad3ed4 100644 --- a/src/components/Tool/FixCommitmentInfo/Contact.tsx +++ b/src/components/Tool/FixCommitmentInfo/Contact.tsx @@ -28,15 +28,14 @@ import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToo import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { TabKey } from 'src/components/Contacts/ContactDetails/ContactDetails'; import { PledgeFrequencyEnum, StatusEnum } from 'src/graphql/types.generated'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { PledgeCurrencyOptionFormatEnum, getPledgeCurrencyOptions, } from 'src/lib/getCurrencyOptions'; import { currencyFormat } from 'src/lib/intlFormat'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; import { getLocalizedPhase } from 'src/utils/functions/getLocalizedPhase'; import { getLocalizedPledgeFrequency } from 'src/utils/functions/getLocalizedPledgeFrequency'; import theme from '../../../theme'; @@ -206,7 +205,7 @@ const Contact: React.FC = ({ const constants = useApiConstants(); const frequencyOptions = constants?.pledgeFrequency; const statusOptions = constants?.status; - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const phases = constants?.phases; const { appName } = useGetAppSettings(); @@ -321,10 +320,7 @@ const Contact: React.FC = ({ {t('Current: {{status}}', { - status: getLocalizedContactStatus( - t, - currentStatus, - ), + status: getLocalizedContactStatus(currentStatus), })} @@ -377,7 +373,7 @@ const Contact: React.FC = ({ , phase?.contactStatuses.map((status) => ( - {contactStatuses[status]?.translated} + {getLocalizedContactStatus(status)} )), ])} diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index e3d425e3b..b851be79f 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -32,9 +32,9 @@ import { EditIcon, LockIcon, } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { useUpdateCache } from 'src/hooks/useUpdateCache'; import { dateFormatShort } from 'src/lib/intlFormat'; import { isEditableSource, sourceToStr } from 'src/utils/sourceHelper'; @@ -141,7 +141,7 @@ const Contact: React.FC = ({ const [setContactPrimaryAddress, { loading: settingPrimaryAddress }] = useSetContactPrimaryAddressMutation(); const { update } = useUpdateCache(id); - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const { appName } = useGetAppSettings(); const handleSetPrimaryContact = async (address: ContactAddressFragment) => { @@ -202,9 +202,7 @@ const Contact: React.FC = ({ } - subheader={ - {contactStatuses[status]?.translated} - } + subheader={{getLocalizedContactStatus(status)}} /> diff --git a/src/components/Tool/FixSendNewsletter/Contact.tsx b/src/components/Tool/FixSendNewsletter/Contact.tsx index 67591e2ca..5a7544afe 100644 --- a/src/components/Tool/FixSendNewsletter/Contact.tsx +++ b/src/components/Tool/FixSendNewsletter/Contact.tsx @@ -22,8 +22,8 @@ import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToo import { SmallLoadingSpinner } from 'src/components/Settings/Organization/LoadingSpinner'; import { SendNewsletterEnum } from 'src/graphql/types.generated'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { dateFormatShort } from 'src/lib/intlFormat'; -import { getLocalizedContactStatus } from 'src/utils/functions/getLocalizedContactStatus'; import { getLocalizedSendNewsletter } from 'src/utils/functions/getLocalizedSendNewsletter'; import { sourceToStr } from 'src/utils/sourceHelper'; import theme from '../../../theme'; @@ -100,6 +100,7 @@ const Contact = ({ const [updatingSingle, setUpdatingSingle] = useState(false); const { classes } = useStyles(); const locale = useLocale(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const matches = useMediaQuery('(min-width:600px)'); useEffect(() => { @@ -190,7 +191,7 @@ const Contact = ({ } subheader={ - {getLocalizedContactStatus(t, contact.status)} + {getLocalizedContactStatus(contact.status)} } > diff --git a/src/components/Tool/MergeContacts/ContactPair.tsx b/src/components/Tool/MergeContacts/ContactPair.tsx index e88d1e2e2..9afca5cc9 100644 --- a/src/components/Tool/MergeContacts/ContactPair.tsx +++ b/src/components/Tool/MergeContacts/ContactPair.tsx @@ -25,8 +25,8 @@ import { DateTime } from 'luxon'; import { TFunction, Trans, useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToolsHelper'; -import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { dateFormatShort } from 'src/lib/intlFormat'; import { sourceToStr } from 'src/utils/sourceHelper'; import theme from '../../../theme'; @@ -145,7 +145,7 @@ const ContactItem: React.FC = ({ side, setContactFocus, }) => { - const { contactStatuses } = useContactPartnershipStatuses(); + const { getLocalizedContactStatus } = useLocalizedConstants(); const { classes } = useStyles(); const locale = useLocale(); @@ -207,7 +207,7 @@ const ContactItem: React.FC = ({ subheader={ isContactType && ( - {contact?.status && contactStatuses[contact?.status]?.translated} + {getLocalizedContactStatus(contact?.status)} ) } diff --git a/src/hooks/useLocalizedConstants.ts b/src/hooks/useLocalizedConstants.ts new file mode 100644 index 000000000..6a2492dc9 --- /dev/null +++ b/src/hooks/useLocalizedConstants.ts @@ -0,0 +1,37 @@ +import { useApiConstants } from 'src/components/Constants/UseApiConstants'; +import { + ContactFilterStatusEnum, + PhaseEnum, + StatusEnum, +} from 'src/graphql/types.generated'; + +export const useLocalizedConstants = () => { + const constants = useApiConstants(); + + const getLocalizedContactStatus = ( + contactStatusEnum: + | StatusEnum + | ContactFilterStatusEnum + | string + | null + | undefined, + ): string => { + return ( + constants?.status?.find((status) => status.id === contactStatusEnum) + ?.value || '' + ); + }; + + const getLocalizedPhase = ( + phaseEnum: PhaseEnum | null | undefined, + ): string => { + return ( + constants?.phases?.find((phase) => phase.id === phaseEnum)?.name || '' + ); + }; + + return { + getLocalizedContactStatus, + getLocalizedPhase, + }; +}; diff --git a/src/utils/functions/getLocalizedContactStatus.ts b/src/utils/functions/getLocalizedContactStatus.ts deleted file mode 100644 index f6b8560c1..000000000 --- a/src/utils/functions/getLocalizedContactStatus.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { TFunction } from 'react-i18next'; -import { - ContactFilterStatusEnum, - StatusEnum, -} from 'src/graphql/types.generated'; - -export const getLocalizedContactStatus = ( - t: TFunction, - contactStatus: StatusEnum | ContactFilterStatusEnum | null | undefined, -): string => { - switch (contactStatus) { - case StatusEnum.AppointmentScheduled: - return t('Appointment Scheduled'); - case StatusEnum.AskInFuture: - return t('Ask in Future'); - case StatusEnum.ResearchContactInfo: - return t('Research Contact Info'); - case StatusEnum.CallForDecision: - return t('Follow Up for Decision'); - case StatusEnum.ContactForAppointment: - return t('Initiate for Appointment'); - case StatusEnum.CultivateRelationship: - return t('Cultivate Relationship'); - case StatusEnum.ExpiredReferral: - return t('Expired Connection'); - case StatusEnum.NeverAsk: - return t('Never Ask'); - case StatusEnum.NeverContacted: - return t('New Connection'); - case StatusEnum.NotInterested: - return t('Not Interested'); - case StatusEnum.PartnerFinancial: - return t('Partner - Financial'); - case StatusEnum.PartnerPray: - return t('Partner - Pray'); - case StatusEnum.PartnerSpecial: - return t('Partner - Special'); - case StatusEnum.ResearchAbandoned: - return t('Research Abandoned'); - case StatusEnum.Unresponsive: - return t('Unresponsive'); - - default: - return contactStatus?.toLowerCase() || ''; - } -}; From cb0de0c95f0776b2da9b426cf9157ed3a89a179c Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 10:54:27 -0700 Subject: [PATCH 05/13] Add useAPIConstants hook to the test setup and remove mocks from tests --- __tests__/util/setup.ts | 14 +++ .../contacts/[[...contactId]].page.test.tsx | 2 - .../LevelOfEffort/LevelOfEffort.test.tsx | 6 +- src/components/Constants/LoadConstantsMock.ts | 54 +++++++++ .../Constants/UseApiConstants.test.tsx | 4 +- .../ContactHeaderStatusSection.test.tsx | 19 +-- .../EditContactOtherModal.test.tsx | 22 ---- .../EditPartnershipInfoModal.test.tsx | 108 ++---------------- .../ContactTaskRow/ContactTaskRow.test.tsx | 54 +-------- .../ContactPartnershipStatus.test.tsx | 25 +--- .../ContactPartnershipStatusLabel.test.tsx | 7 +- .../ContactsLeftPanel.test.tsx | 2 - .../ContactsMap/ContactsMapPanel.test.tsx | 14 +-- .../MassActionsEditFieldsModal.test.tsx | 9 +- .../TasksDueThisWeek.test.tsx | 17 +-- .../CreateMultipleContacts.test.tsx | 4 - .../Items/SearchMenu/SearchMenu.test.tsx | 10 +- .../Modals/EditGoogleAccountModal.test.tsx | 56 ++------- .../CurrencyAccordion.test.tsx | 32 +----- .../HourToSendNotificationsAccordion.test.tsx | 24 +--- .../LocaleAccordion/LocaleAccordion.test.tsx | 27 +---- .../Shared/Filters/FilterListItem.test.tsx | 10 +- .../Shared/Filters/FilterPanel.test.tsx | 13 +-- .../TasksMassActionsDropdown.test.tsx | 8 +- .../MassActionsEditTasksModal.test.tsx | 12 +- .../Complete/TaskModalCompleteForm.test.tsx | 4 - .../ActivityTypeAutocomplete.test.tsx | 25 ++-- .../SuggestedContactStatus.test.tsx | 4 - .../Form/LogForm/TaskModalLogForm.test.tsx | 57 +-------- .../Task/Modal/Form/TaskModalForm.test.tsx | 26 +---- src/components/Task/Modal/TaskModal.test.tsx | 4 +- .../AddAppealForm/AddAppealForm.test.tsx | 4 - .../Tool/FixCommitmentInfo/Contact.test.tsx | 10 +- .../FixCommitmentInfo.test.tsx | 4 - 34 files changed, 134 insertions(+), 557 deletions(-) diff --git a/__tests__/util/setup.ts b/__tests__/util/setup.ts index 5531f4bb1..dfd653654 100644 --- a/__tests__/util/setup.ts +++ b/__tests__/util/setup.ts @@ -3,9 +3,17 @@ import 'isomorphic-fetch'; import { Settings } from 'luxon'; import { type useSession } from 'next-auth/react'; import { session } from '__tests__/fixtures/session'; +import { + LoadConstantsDocument, + LoadConstantsQuery, +} from 'src/components/Constants/LoadConstants.generated'; +import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; +import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { toHaveGraphqlOperation } from '../extensions/toHaveGraphqlOperation'; +import { gqlMock } from './graphqlMocking'; import matchMediaMock from './matchMediaMock'; +jest.mock('src/components/Constants/UseApiConstants'); jest.mock('next-auth/react', () => { return { getSession: jest.fn().mockResolvedValue(session), @@ -21,6 +29,12 @@ jest.mock('next-auth/react', () => { }; }); +(useApiConstants as jest.MockedFn).mockReturnValue( + gqlMock(LoadConstantsDocument, { + mocks: loadConstantsMockData, + }).constant, +); + expect.extend({ toHaveGraphqlOperation, }); diff --git a/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.test.tsx b/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.test.tsx index 8b2eb0be1..7a9a89a86 100644 --- a/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.test.tsx +++ b/pages/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]].page.test.tsx @@ -7,7 +7,6 @@ import { SnackbarProvider } from 'notistack'; import { I18nextProvider } from 'react-i18next'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { GetContactDuplicatesQuery } from 'src/components/Tool/MergeContacts/GetContactDuplicates.generated'; import { getContactDuplicatesMocks } from 'src/components/Tool/MergeContacts/MergeContactsMock'; import i18n from 'src/lib/i18n'; @@ -48,7 +47,6 @@ const Components = () => ( }> mocks={{ ...getContactDuplicatesMocks, - LoadConstants: loadConstantsMockData, }} > diff --git a/src/components/Coaching/CoachingDetail/LevelOfEffort/LevelOfEffort.test.tsx b/src/components/Coaching/CoachingDetail/LevelOfEffort/LevelOfEffort.test.tsx index 9f85fdce7..faf7a895b 100644 --- a/src/components/Coaching/CoachingDetail/LevelOfEffort/LevelOfEffort.test.tsx +++ b/src/components/Coaching/CoachingDetail/LevelOfEffort/LevelOfEffort.test.tsx @@ -1,6 +1,5 @@ import { render, waitFor } from '@testing-library/react'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { CoachingPeriodEnum } from '../CoachingDetail'; import { levelOfEffortMocks } from '../coachingMocks'; import { LevelOfEffort } from './LevelOfEffort'; @@ -13,7 +12,6 @@ describe('LevelOfEffort', () => { { ); await waitFor(() => - expect(mutationSpy.mock.calls[1][0].operation.variables).toMatchObject({ + expect(mutationSpy.mock.calls[0][0].operation.variables).toMatchObject({ range: '4w', }), ); @@ -134,7 +132,7 @@ describe('LevelOfEffort', () => { ); await waitFor(() => - expect(mutationSpy.mock.calls[1][0].operation.variables).toMatchObject({ + expect(mutationSpy.mock.calls[0][0].operation.variables).toMatchObject({ range: '4m', }), ); diff --git a/src/components/Constants/LoadConstantsMock.ts b/src/components/Constants/LoadConstantsMock.ts index bb1b6f4b4..f7361d4ee 100644 --- a/src/components/Constants/LoadConstantsMock.ts +++ b/src/components/Constants/LoadConstantsMock.ts @@ -898,6 +898,11 @@ export const loadConstantsMockData: LoadConstantsQuery = { symbol: '$', value: 'USD ($)', }, + { + code: 'EUR', + codeSymbolString: 'EUR (€)', + name: 'Euro', + }, ], pledgeFrequency: [ { @@ -911,6 +916,55 @@ export const loadConstantsMockData: LoadConstantsQuery = { value: 'Annual', }, ], + languages: [ + { + id: 'en', + value: 'English', + }, + { + id: 'elx', + value: 'Greek', + }, + { + id: 'eka', + value: 'Ekajuk', + }, + { + id: 'en-AU', + value: 'Australian English', + }, + ], + locales: [ + { + englishName: 'Filipino (fil)', + nativeName: 'Filipino', + shortName: 'fil', + }, + { + englishName: 'UK English (en-GB)', + nativeName: 'UK English', + shortName: 'en-GB', + }, + { + englishName: 'Latin American Spanish (es-419)', + nativeName: 'español latinoamericano', + shortName: 'es-419', + }, + ], + times: [ + { + key: 0, + value: '12:00 AM', + }, + { + key: 5, + value: '5:00 AM', + }, + { + key: null, + value: 'Immediately', + }, + ], }, }; diff --git a/src/components/Constants/UseApiConstants.test.tsx b/src/components/Constants/UseApiConstants.test.tsx index af2ac2546..0398d9a85 100644 --- a/src/components/Constants/UseApiConstants.test.tsx +++ b/src/components/Constants/UseApiConstants.test.tsx @@ -4,12 +4,10 @@ import { useApiConstants } from './UseApiConstants'; describe('LoadConstants', () => { it('returns an object', async () => { - const { result, waitForNextUpdate } = renderHook(() => useApiConstants(), { + const { result } = renderHook(() => useApiConstants(), { wrapper: GqlMockedProvider, }); - await waitForNextUpdate(); - expect(result.current?.activities).toBeTruthy(); expect(result.current?.languages).toBeTruthy(); expect(result.current?.likelyToGive).toBeTruthy(); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.test.tsx index 62b8378cd..2ba445425 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsHeader/ContactHeaderSection/ContactHeaderStatusSection.test.tsx @@ -2,8 +2,7 @@ import React from 'react'; import { ThemeProvider } from '@mui/material/styles'; import { render } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; -import { GqlMockedProvider, gqlMock } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; +import { gqlMock } from '__tests__/util/graphqlMocking'; import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { PledgeFrequencyEnum, StatusEnum } from 'src/graphql/types.generated'; import i18n from '../../../../../lib/i18n'; @@ -53,17 +52,11 @@ interface ComponentsProps { } const Components = ({ loading, contact }: ComponentsProps) => ( - - mocks={{ LoadConstants: loadConstantsMockData }} - > - - - - - - + + + + + ); describe('ContactHeaderStatusSection', () => { diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Other/EditContactOtherModal/EditContactOtherModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Other/EditContactOtherModal/EditContactOtherModal.test.tsx index fd9337d2d..d95097bbc 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Other/EditContactOtherModal/EditContactOtherModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Other/EditContactOtherModal/EditContactOtherModal.test.tsx @@ -426,28 +426,6 @@ describe('EditContactOtherModal', () => { }> onCall={mutationSpy} mocks={{ - LoadConstants: { - constant: { - languages: [ - { - id: 'en', - value: 'English', - }, - { - id: 'elx', - value: 'Greek', - }, - { - id: 'eka', - value: 'Ekajuk', - }, - { - id: 'en-AU', - value: 'Australian English', - }, - ], - }, - }, AssigneeOptions: { accountListUsers: { nodes: [ diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.test.tsx index 7c5bbc46a..de1ae41e7 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/EditPartnershipInfoModal/EditPartnershipInfoModal.test.tsx @@ -6,11 +6,6 @@ import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { SnackbarProvider } from 'notistack'; import { GqlMockedProvider, gqlMock } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { - loadConstantsMockData as LoadConstants, - loadConstantsMockData, -} from 'src/components/Constants/LoadConstantsMock'; import { LikelyToGiveEnum, PledgeFrequencyEnum, @@ -23,7 +18,6 @@ import { ContactDonorAccountsFragmentDoc, } from '../../ContactDonationsTab.generated'; import { EditPartnershipInfoModal } from './EditPartnershipInfoModal'; -import { UpdateContactPartnershipMutation } from './EditPartnershipInfoModal.generated'; const handleClose = jest.fn(); const contactMock = gqlMock( @@ -196,11 +190,7 @@ describe('EditPartnershipInfoModal', () => { - - mocks={{ LoadConstants }} - > + { - - mocks={{ LoadConstants }} - > + { - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { - - mocks={{ LoadConstants }} - > + { - - mocks={{ - LoadConstants: { - constant: { - pledgeCurrency: [ - { - code: 'CAD', - codeSymbolString: 'CAD ($)', - name: 'Canadian Dollar', - }, - { - code: 'CDF', - codeSymbolString: 'CDF (CDF)', - name: 'Congolese Franc', - }, - { - code: 'CHE', - codeSymbolString: 'CHE (CHE)', - name: 'WIR Euro', - }, - ], - }, - }, - }} - > + { - - mocks={{ - LoadConstants: { - constant: { - pledgeCurrency: [ - { - id: 'CAD', - value: 'CAD ($)', - }, - { - id: 'CDF', - value: 'CDF (CDF)', - }, - { - id: 'CHE', - value: 'CHE (CHE)', - }, - ], - }, - }, - }} - > + { - - mocks={{ - LoadConstants: { - constant: { - pledgeCurrency: [ - { - id: 'CAD', - value: 'CAD ($)', - }, - { - id: 'CDF', - value: 'CDF (CDF)', - }, - { - id: 'CHE', - value: 'CHE (CHE)', - }, - ], - }, - }, - }} - > + { ); }); }); - - describe('activity type', () => { - it.each([ - { activityType: ActivityTypeEnum.AppointmentInPerson, name: 'In Person' }, - { - activityType: ActivityTypeEnum.InitiationPhoneCall, - name: 'Phone Call', - }, - { activityType: ActivityTypeEnum.InitiationEmail, name: 'Email' }, - { - activityType: ActivityTypeEnum.InitiationSocialMedia, - name: 'Social Media', - }, - { - activityType: ActivityTypeEnum.PartnerCareDigitalNewsletter, - name: 'Digital Newsletter', - }, - { - activityType: ActivityTypeEnum.PartnerCarePhysicalNewsletter, - name: 'Physical Newsletter', - }, - { - activityType: ActivityTypeEnum.PartnerCarePrayerRequest, - name: 'Prayer Request', - }, - { activityType: ActivityTypeEnum.InitiationLetter, name: 'Letter' }, - { - activityType: ActivityTypeEnum.InitiationSpecialGiftAppeal, - name: 'Special Gift Appeal', - }, - { activityType: ActivityTypeEnum.PartnerCareInPerson, name: 'In Person' }, - { - activityType: ActivityTypeEnum.FollowUpTextMessage, - name: 'Text Message', - }, - { - activityType: ActivityTypeEnum.PartnerCareThank, - name: 'Thank You Note', - }, - { activityType: ActivityTypeEnum.PartnerCareToDo, name: 'To Do' }, - ])('displays $name', ({ activityType, name }) => { - const task = gqlMock(TaskRowFragmentDoc, { - mocks: { - activityType, - }, - }); - - const { getByText } = render(); - - expect(getByText(name)).toBeVisible(); - }); - }); }); diff --git a/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatus.test.tsx b/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatus.test.tsx index 9087a4cc1..05238f9e0 100644 --- a/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatus.test.tsx +++ b/src/components/Contacts/ContactPartnershipStatus/ContactPartnershipStatus.test.tsx @@ -3,7 +3,6 @@ import { ThemeProvider } from '@mui/material/styles'; import { render } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { StatusEnum } from 'src/graphql/types.generated'; import i18n from 'src/lib/i18n'; @@ -15,11 +14,7 @@ const status = StatusEnum.PartnerFinancial; describe('ContactPartnershipStatus', () => { it('default', async () => { const { findByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { it('render partner pray', async () => { const { findByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { describe('pledge amount', () => { it('renders pledge amount', () => { const { getByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { it('does not render pledge amount when 0', () => { const { queryByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { it('default', async () => { const { findByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + diff --git a/src/components/Contacts/ContactsLeftPanel/ContactsLeftPanel.test.tsx b/src/components/Contacts/ContactsLeftPanel/ContactsLeftPanel.test.tsx index fd53d2971..4cd3baf6e 100644 --- a/src/components/Contacts/ContactsLeftPanel/ContactsLeftPanel.test.tsx +++ b/src/components/Contacts/ContactsLeftPanel/ContactsLeftPanel.test.tsx @@ -5,7 +5,6 @@ import userEvent from '@testing-library/user-event'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { ContactsWrapper } from 'pages/accountLists/[accountListId]/contacts/ContactsWrapper'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { ContactsContext, ContactsType, @@ -58,7 +57,6 @@ const mocks = { ContactFilters: { userOptions, }, - LoadConstants: loadConstantsMockData, }; describe('ContactsLeftPanel', () => { diff --git a/src/components/Contacts/ContactsMap/ContactsMapPanel.test.tsx b/src/components/Contacts/ContactsMap/ContactsMapPanel.test.tsx index 3a97fddc4..19062f85a 100644 --- a/src/components/Contacts/ContactsMap/ContactsMapPanel.test.tsx +++ b/src/components/Contacts/ContactsMap/ContactsMapPanel.test.tsx @@ -5,8 +5,6 @@ import userEvent from '@testing-library/user-event'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { ContactsWrapper } from 'pages/accountLists/[accountListId]/contacts/ContactsWrapper'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { StatusEnum } from 'src/graphql/types.generated'; import theme from '../../../theme'; import { ContactsMapPanel } from './ContactsMapPanel'; @@ -51,11 +49,7 @@ describe('ContactsMapPanel', () => { const { getByText, queryByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { const { getByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + { it('Select status and starred, the save action', async () => { const mutationSpy = jest.fn(); const { getByRole, queryByTestId, queryByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - onCall={mutationSpy} - > + diff --git a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx index 47a1f3193..3527ebecd 100644 --- a/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx +++ b/src/components/Dashboard/ThisWeek/TasksDueThisWeek/TasksDueThisWeek.test.tsx @@ -3,8 +3,6 @@ import { ThemeProvider } from '@mui/material/styles'; import userEvent from '@testing-library/user-event'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { render } from '__tests__/util/testingLibraryReactMock'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { TaskModalEnum } from 'src/components/Task/Modal/TaskModal'; import { ActivityTypeEnum } from 'src/graphql/types.generated'; import useTaskModal from '../../../../hooks/useTaskModal'; @@ -104,9 +102,7 @@ describe('TasksDueThisWeek', () => { }; const { getByTestId, queryByTestId, getByText, findByText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + , @@ -167,16 +163,7 @@ describe('TasksDueThisWeek', () => { }; const { getByTestId, getByText } = render( - - mocks={{ - constant: { - activities: [ - { id: 'Prayer Request', value: 'Prayer Request' }, - { id: 'Appointment', value: 'Appointment' }, - ], - }, - }} - > + , diff --git a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx index b91b0dc4b..5403101e2 100644 --- a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx +++ b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx @@ -6,7 +6,6 @@ import { SnackbarProvider } from 'notistack'; import TestRouter from '__tests__/util/TestRouter'; import { placePromise, setupMocks } from '__tests__/util/googlePlacesMock'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { CreateContactAddressMutation } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/CreateContactAddress.generated'; import { StatusEnum } from 'src/graphql/types.generated'; @@ -19,7 +18,6 @@ jest.mock('@react-google-maps/api'); interface CreateContactMocks { CreateContact: CreateContactMutation; CreateContactAddress: CreateContactAddressMutation; - LoadConstants: LoadConstantsQuery; } const accountListId = '111'; @@ -433,7 +431,6 @@ describe('CreateMultipleContacts', () => { onCall={mutationSpy} mocks={{ - LoadConstants: loadConstantsMockData, CreateContact: { createContact: { contact: { @@ -514,7 +511,6 @@ describe('CreateMultipleContacts', () => { onCall={mutationSpy} mocks={{ - LoadConstants: loadConstantsMockData, CreateContact: { createContact: { contact: { diff --git a/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchMenu.test.tsx b/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchMenu.test.tsx index 61184d9c8..045c9b462 100644 --- a/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchMenu.test.tsx +++ b/src/components/Layouts/Primary/TopBar/Items/SearchMenu/SearchMenu.test.tsx @@ -5,8 +5,6 @@ import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { StatusEnum } from 'src/graphql/types.generated'; import i18n from 'src/lib/i18n'; import theme from 'src/theme'; @@ -36,11 +34,7 @@ jest.mock('notistack', () => ({ describe('SearchMenu', () => { it('default', async () => { const { getByRole, getByPlaceholderText } = render( - - mocks={{ LoadConstants: loadConstantsMockData }} - > + @@ -95,7 +89,6 @@ describe('SearchMenu', () => { totalCount: 8, }, }, - LoadConstants: loadConstantsMockData, }} > @@ -147,7 +140,6 @@ describe('SearchMenu', () => { ], }, }, - LoadConstants: loadConstantsMockData, }} > diff --git a/src/components/Settings/integrations/Google/Modals/EditGoogleAccountModal.test.tsx b/src/components/Settings/integrations/Google/Modals/EditGoogleAccountModal.test.tsx index a09e9d2e8..6baf59bf9 100644 --- a/src/components/Settings/integrations/Google/Modals/EditGoogleAccountModal.test.tsx +++ b/src/components/Settings/integrations/Google/Modals/EditGoogleAccountModal.test.tsx @@ -5,7 +5,6 @@ import userEvent from '@testing-library/user-event'; import { SnackbarProvider } from 'notistack'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; import { ActivityTypeEnum, GoogleAccountIntegration, @@ -194,31 +193,12 @@ describe('EditGoogleAccountModal', () => { const { getByText, getByRole, findByRole, queryByRole } = render( mocks={{ GoogleAccountIntegrations: { googleAccountIntegrations: [googleIntegration], }, - LoadConstants: { - constant: { - activities: [ - { - value: ActivityTypeEnum.AppointmentVideoCall, - id: ActivityTypeEnum.AppointmentVideoCall, - }, - { - value: ActivityTypeEnum.AppointmentInPerson, - id: ActivityTypeEnum.AppointmentInPerson, - }, - { - value: ActivityTypeEnum.FollowUpEmail, - id: ActivityTypeEnum.FollowUpEmail, - }, - ], - }, - }, }} onCall={mutationSpy} > @@ -286,30 +266,11 @@ describe('EditGoogleAccountModal', () => { mocks={{ GoogleAccountIntegrations: { googleAccountIntegrations: [googleIntegration], }, - LoadConstants: { - constant: { - activities: [ - { - value: ActivityTypeEnum.AppointmentVideoCall, - id: ActivityTypeEnum.AppointmentVideoCall, - }, - { - value: ActivityTypeEnum.AppointmentInPerson, - id: ActivityTypeEnum.AppointmentInPerson, - }, - { - value: ActivityTypeEnum.FollowUpEmail, - id: ActivityTypeEnum.FollowUpEmail, - }, - ], - }, - }, }} onCall={mutationSpy} > @@ -330,11 +291,11 @@ describe('EditGoogleAccountModal', () => { await waitFor(() => expect( - getByTestId('APPOINTMENT_VIDEO_CALL-Checkbox'), + getByTestId('Appointment - Video Call-Checkbox'), ).toBeInTheDocument(), ); - userEvent.click(getByTestId('APPOINTMENT_VIDEO_CALL-Checkbox')); + userEvent.click(getByTestId('Appointment - Video Call-Checkbox')); userEvent.click(getByRole('button', { name: /update/i })); await waitFor(() => @@ -510,13 +471,12 @@ describe('EditGoogleAccountModal', () => { variant: 'success', }, ); - expect(mutationSpy.mock.calls[2][0].operation.operationName).toEqual( - 'SyncGoogleAccount', - ); - expect(mutationSpy.mock.calls[2][0].operation.variables.input).toEqual({ - googleAccountId: googleAccount.id, - integrationName: 'calendar', - googleIntegrationId: googleIntegration.id, + expect(mutationSpy).toHaveGraphqlOperation('SyncGoogleAccount', { + input: { + googleAccountId: googleAccount.id, + integrationName: 'calendar', + googleIntegrationId: googleIntegration.id, + }, }); }); }); diff --git a/src/components/Settings/preferences/accordions/CurrencyAccordion/CurrencyAccordion.test.tsx b/src/components/Settings/preferences/accordions/CurrencyAccordion/CurrencyAccordion.test.tsx index 7a1431619..4c49daafe 100644 --- a/src/components/Settings/preferences/accordions/CurrencyAccordion/CurrencyAccordion.test.tsx +++ b/src/components/Settings/preferences/accordions/CurrencyAccordion/CurrencyAccordion.test.tsx @@ -41,37 +41,7 @@ const Components: React.FC = ({ currency, expandedPanel }) => ( - + = ({ - + = ({ - + { it('MultiSelectFilter blank', () => { const { getAllByText, getByTestId } = render( - - mocks={{ - LoadConstants: loadConstantsMockData, - }} - > + { render( - + { render( - + ({ const TaskComponents = () => ( - - mocks={{ LoadConstants }} - > + { ); userEvent.click(getByRole('button', { name: 'Save' })); await waitFor(() => { - expect(mutationSpy.mock.calls[3][0]).toMatchObject({ + expect(mutationSpy.mock.calls[2][0]).toMatchObject({ operation: { operationName: 'CreateTaskComment', variables: { @@ -52,7 +50,7 @@ describe('MassActionsEditTasksModal', () => { }, }, }); - expect(mutationSpy.mock.calls[4][0]).toMatchObject({ + expect(mutationSpy.mock.calls[3][0]).toMatchObject({ operation: { operationName: 'CreateTaskComment', variables: { @@ -96,11 +94,7 @@ it('shows correct Action field options based on the Task Type', async () => { - - mocks={{ LoadConstants: loadConstantsMockData }} - > + ( mocks={{ - LoadConstants: loadConstantsMockData, ContactStatus: { contact: { status: null, diff --git a/src/components/Task/Modal/Form/Inputs/ActivityTypeAutocomplete/ActivityTypeAutocomplete.test.tsx b/src/components/Task/Modal/Form/Inputs/ActivityTypeAutocomplete/ActivityTypeAutocomplete.test.tsx index 08ef6cb86..6c6a9b2b2 100644 --- a/src/components/Task/Modal/Form/Inputs/ActivityTypeAutocomplete/ActivityTypeAutocomplete.test.tsx +++ b/src/components/Task/Modal/Form/Inputs/ActivityTypeAutocomplete/ActivityTypeAutocomplete.test.tsx @@ -1,11 +1,6 @@ import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { - loadConstantsMockData as LoadConstants, - loadConstantsMockData, -} from 'src/components/Constants/LoadConstantsMock'; +import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { ActivityTypeEnum } from 'src/graphql/types.generated'; import i18n from 'src/lib/i18n'; import { getLocalizedPhase } from 'src/utils/functions/getLocalizedPhase'; @@ -55,18 +50,12 @@ describe('ActivityTypeAutocomplete', () => { const onChange = jest.fn(); const { getByRole } = render( - - mocks={{ LoadConstants }} - > - - , + , ); const input = getByRole('combobox', { name: 'Type' }); diff --git a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.test.tsx b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.test.tsx index 1507c7213..7af9bcc38 100644 --- a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.test.tsx +++ b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.test.tsx @@ -3,8 +3,6 @@ import { ThemeProvider } from '@emotion/react'; import { render, waitFor } from '@testing-library/react'; import { I18nextProvider } from 'react-i18next'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { StatusEnum } from 'src/graphql/types.generated'; import i18n from 'src/lib/i18n'; import theme from 'src/theme'; @@ -34,11 +32,9 @@ const Components = ({ mocks={{ - LoadConstants: loadConstantsMockData, ContactStatus: { contact: { status: contactStatusQueryMock || null, diff --git a/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx b/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx index 369008a1d..64cdf66b8 100644 --- a/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx +++ b/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx @@ -7,11 +7,6 @@ import userEvent from '@testing-library/user-event'; import { SnackbarProvider } from 'notistack'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { - loadConstantsMockData as LoadConstants, - loadConstantsMockData, -} from 'src/components/Constants/LoadConstantsMock'; import { AssigneeOptionsQuery } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Other/EditContactOtherModal/EditContactOther.generated'; import { GetUserQuery } from 'src/components/User/GetUser.generated'; import { ActivityTypeEnum } from 'src/graphql/types.generated'; @@ -63,11 +58,7 @@ describe('TaskModalLogForm', () => { - + { })), }, }, - LoadConstants: loadConstantsMockData, }} > { - - mocks={{ - LoadConstants, - }} - addTypename={false} - > + { AssigneeOptions: AssigneeOptionsQuery; ContactOptions: ContactOptionsQuery; TagOptions: TagOptionsQuery; - LoadConstants: LoadConstantsQuery; }> mocks={{ AssigneeOptions: { @@ -247,7 +229,6 @@ describe('TaskModalLogForm', () => { taskTagList: ['tag-1', 'tag-2'], }, }, - LoadConstants: LoadConstants, }} onCall={mutationSpy} > @@ -344,13 +325,7 @@ describe('TaskModalLogForm', () => { const { getByRole, findByRole, queryByRole } = render( - - mocks={{ - LoadConstants, - }} - > + { - - mocks={{ - LoadConstants, - }} - > + { - - onCall={mutationSpy} - mocks={{ - LoadConstants, - }} - > + { - - onCall={mutationSpy} - mocks={{ - LoadConstants, - }} - > + { AssigneeOptions: AssigneeOptionsQuery; ContactOptions: ContactOptionsQuery; TagOptions: TagOptionsQuery; - LoadConstants: LoadConstantsQuery; }> mocks={{ AssigneeOptions: { @@ -156,7 +152,6 @@ describe('TaskModalForm', () => { taskTagList: ['tag-1', 'tag-2'], }, }, - LoadConstants, }} onCall={mutationSpy} > @@ -227,7 +222,6 @@ describe('TaskModalForm', () => { AssigneeOptions: AssigneeOptionsQuery; ContactOptions: ContactOptionsQuery; TagOptions: TagOptionsQuery; - LoadConstants: LoadConstantsQuery; }> mocks={{ AssigneeOptions: { @@ -255,7 +249,6 @@ describe('TaskModalForm', () => { taskTagList: ['tag-1', 'tag-2'], }, }, - LoadConstants, }} onCall={mutationSpy} > @@ -392,14 +385,7 @@ describe('TaskModalForm', () => { - - mocks={{ - LoadConstants, - }} - onCall={mutationSpy} - > + { - - mocks={{ - LoadConstants, - }} - > + { const dialog = getByRole('dialog', { name: 'Complete Task', }); - expect(within(dialog).getByText('Prayer Request')).toBeInTheDocument(); + expect( + within(dialog).getByText('Partner Care - Prayer Request'), + ).toBeInTheDocument(); }); }); diff --git a/src/components/Tool/Appeal/InitialPage/AddAppealForm/AddAppealForm.test.tsx b/src/components/Tool/Appeal/InitialPage/AddAppealForm/AddAppealForm.test.tsx index a2daaf3f6..d65e295e6 100644 --- a/src/components/Tool/Appeal/InitialPage/AddAppealForm/AddAppealForm.test.tsx +++ b/src/components/Tool/Appeal/InitialPage/AddAppealForm/AddAppealForm.test.tsx @@ -9,8 +9,6 @@ import { I18nextProvider } from 'react-i18next'; import TestRouter from '__tests__/util/TestRouter'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { AppealsWrapper } from 'pages/accountLists/[accountListId]/tools/appeals/AppealsWrapper'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import i18n from 'src/lib/i18n'; import theme from 'src/theme'; import AddAppealForm, { @@ -57,11 +55,9 @@ const Components = ({ mocks={{ ContactTags: contactTagsMock, - LoadConstants: loadConstantsMockData, }} onCall={mutationSpy} > diff --git a/src/components/Tool/FixCommitmentInfo/Contact.test.tsx b/src/components/Tool/FixCommitmentInfo/Contact.test.tsx index d2b77991b..d223b941b 100644 --- a/src/components/Tool/FixCommitmentInfo/Contact.test.tsx +++ b/src/components/Tool/FixCommitmentInfo/Contact.test.tsx @@ -5,8 +5,6 @@ import TestRouter from '__tests__/util/TestRouter'; import TestWrapper from '__tests__/util/TestWrapper'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { fireEvent, render } from '__tests__/util/testingLibraryReactMock'; -import { LoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { TabKey } from 'src/components/Contacts/ContactDetails/ContactDetails'; import { PledgeFrequencyEnum, StatusEnum } from 'src/graphql/types.generated'; import theme from '../../../theme'; @@ -50,13 +48,7 @@ const TestComponent = ({ }) => ( - - mocks={{ - LoadConstants: loadConstantsMockData, - }} - > + mocks={{ - LoadConstants: loadConstantsMockData, InvalidStatuses: { contacts: { nodes: mockNodes, From cf5f9620c195e57bebbfc2832c1384aecb99d9cd Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 12:59:08 -0700 Subject: [PATCH 06/13] Add tests for hook and helper functions --- .../useContactPartnershipStatuses.test.ts | 33 ++++++++++--------- src/hooks/useLocalizedConstants.test.ts | 20 +++++++++++ .../functions/convertContactStatus.test.ts | 23 +++++++++++++ 3 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 src/hooks/useLocalizedConstants.test.ts create mode 100644 src/utils/functions/convertContactStatus.test.ts diff --git a/src/hooks/useContactPartnershipStatuses.test.ts b/src/hooks/useContactPartnershipStatuses.test.ts index af7ac0231..488433b1b 100644 --- a/src/hooks/useContactPartnershipStatuses.test.ts +++ b/src/hooks/useContactPartnershipStatuses.test.ts @@ -1,22 +1,7 @@ import { renderHook } from '@testing-library/react-hooks'; -import { gqlMock } from '__tests__/util/graphqlMocking'; -import { - LoadConstantsDocument, - LoadConstantsQuery, -} from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; -import { useApiConstants } from 'src/components/Constants/UseApiConstants'; +import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; import { useContactPartnershipStatuses } from './useContactPartnershipStatuses'; -jest.mock('src/components/Constants/UseApiConstants.tsx'); - -// Mock useApiConstants to make the data available synchronously instead of having to wait for the GraphQL call -(useApiConstants as jest.MockedFn).mockReturnValue( - gqlMock(LoadConstantsDocument, { - mocks: loadConstantsMockData, - }).constant, -); - describe('useContactPartnershipStatuses', () => { it('should return correctly formatted contactPartnershipStatuses.test', () => { const { result } = renderHook(() => useContactPartnershipStatuses()); @@ -81,4 +66,20 @@ describe('useContactPartnershipStatuses', () => { null: 'NULL', }); }); + describe('getContactStatusesByPhase', () => { + it('returns array of Statuses', () => { + const { result } = renderHook(() => useContactPartnershipStatuses()); + expect( + result.current.getContactStatusesByPhase(PhaseEnum.PartnerCare), + ).toEqual([ + StatusEnum.PartnerFinancial, + StatusEnum.PartnerSpecial, + StatusEnum.PartnerPray, + ]); + }); + it('returns empty array when invalid', () => { + const { result } = renderHook(() => useContactPartnershipStatuses()); + expect(result.current.getContactStatusesByPhase('invalid')).toEqual([]); + }); + }); }); diff --git a/src/hooks/useLocalizedConstants.test.ts b/src/hooks/useLocalizedConstants.test.ts new file mode 100644 index 000000000..cfc973006 --- /dev/null +++ b/src/hooks/useLocalizedConstants.test.ts @@ -0,0 +1,20 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { StatusEnum } from 'src/graphql/types.generated'; +import { useLocalizedConstants } from './useLocalizedConstants'; + +describe('useLocalizedConstants', () => { + describe('getLocalizedContactStatus', () => { + it('should return correctly formatted contact statuses', () => { + const { result } = renderHook(() => useLocalizedConstants()); + expect( + result.current.getLocalizedContactStatus( + StatusEnum.ContactForAppointment, + ), + ).toEqual('Initiate for Appointment'); + }); + it('should return empty string for invalid statuses', () => { + const { result } = renderHook(() => useLocalizedConstants()); + expect(result.current.getLocalizedContactStatus('invalid')).toEqual(''); + }); + }); +}); diff --git a/src/utils/functions/convertContactStatus.test.ts b/src/utils/functions/convertContactStatus.test.ts new file mode 100644 index 000000000..9440e7551 --- /dev/null +++ b/src/utils/functions/convertContactStatus.test.ts @@ -0,0 +1,23 @@ +import { StatusEnum } from 'src/graphql/types.generated'; +import { convertStatus } from './convertContactStatus'; + +describe('convertContactStatus', () => { + it('converts to StatusEnum or null', () => { + // old status + expect(convertStatus('Partner - Financial')).toEqual( + StatusEnum.PartnerFinancial, + ); + // new lowercase status + expect(convertStatus('partner_financial')).toEqual( + StatusEnum.PartnerFinancial, + ); + // uppercase ENUM + expect(convertStatus('CONTACT_FOR_APPOINTMENT')).toEqual( + StatusEnum.ContactForAppointment, + ); + // invalid + expect(convertStatus('hidden')).toEqual(null); + expect(convertStatus(undefined)).toEqual(null); + expect(convertStatus('')).toEqual(null); + }); +}); From 03744e8eb6fed4ab281b8626a9669af016d4ffd6 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 17:59:15 -0700 Subject: [PATCH 07/13] Add useCallback --- src/components/Shared/Filters/FilterPanel.tsx | 6 +-- .../SuggestedContactStatus.tsx | 2 +- src/hooks/useContactPartnershipStatuses.ts | 18 ++++---- src/hooks/useLocalizedConstants.ts | 45 ++++++++++--------- src/utils/functions/convertContactStatus.ts | 9 ++-- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/src/components/Shared/Filters/FilterPanel.tsx b/src/components/Shared/Filters/FilterPanel.tsx index 10055c89e..5c278ae36 100644 --- a/src/components/Shared/Filters/FilterPanel.tsx +++ b/src/components/Shared/Filters/FilterPanel.tsx @@ -156,9 +156,9 @@ export const FilterPanel: React.FC = ({ const matchFilterContactStatuses = (status: string | null | undefined) => { return ( - Object.values(ContactFilterStatusEnum).find((value) => { - return String(value.toLowerCase()) === String(status?.toLowerCase()); - }) || null + Object.values(ContactFilterStatusEnum).find( + (value) => value === status?.toUpperCase(), + ) || null ); }; diff --git a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx index ffb331b3b..afb8fc34f 100644 --- a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx +++ b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx @@ -65,7 +65,7 @@ export const SuggestedContactStatus: React.FC = ({ } // Show suggestedStatus if the contact has no status return true; - }, [currentContactStatus]); + }, [currentContactStatus, suggestedContactStatus, getContactStatusesByPhase]); return suggestedContactStatus && shouldRenderContactSuggestion ? ( diff --git a/src/hooks/useContactPartnershipStatuses.ts b/src/hooks/useContactPartnershipStatuses.ts index ef2d10de8..c549d441d 100644 --- a/src/hooks/useContactPartnershipStatuses.ts +++ b/src/hooks/useContactPartnershipStatuses.ts @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; @@ -111,13 +111,15 @@ export const useContactPartnershipStatuses = () => { return { id: statusKey, ...s }; }); - const getContactStatusesByPhase = ( - phase: PhaseEnum | string | null, - ): StatusEnum[] => { - return ( - phases?.find((p) => String(p.id) === String(phase))?.contactStatuses || [] - ); - }; + const getContactStatusesByPhase = useCallback( + (phase: PhaseEnum | string | null): StatusEnum[] => { + return ( + phases?.find((p) => String(p.id) === String(phase))?.contactStatuses || + [] + ); + }, + [phases], + ); return { contactStatuses, diff --git a/src/hooks/useLocalizedConstants.ts b/src/hooks/useLocalizedConstants.ts index 6a2492dc9..ce7c8f422 100644 --- a/src/hooks/useLocalizedConstants.ts +++ b/src/hooks/useLocalizedConstants.ts @@ -1,3 +1,4 @@ +import { useCallback } from 'react'; import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { ContactFilterStatusEnum, @@ -8,27 +9,31 @@ import { export const useLocalizedConstants = () => { const constants = useApiConstants(); - const getLocalizedContactStatus = ( - contactStatusEnum: - | StatusEnum - | ContactFilterStatusEnum - | string - | null - | undefined, - ): string => { - return ( - constants?.status?.find((status) => status.id === contactStatusEnum) - ?.value || '' - ); - }; + const getLocalizedContactStatus = useCallback( + ( + contactStatusEnum: + | StatusEnum + | ContactFilterStatusEnum + | string + | null + | undefined, + ): string => { + return ( + constants?.status?.find((status) => status.id === contactStatusEnum) + ?.value || '' + ); + }, + [constants], + ); - const getLocalizedPhase = ( - phaseEnum: PhaseEnum | null | undefined, - ): string => { - return ( - constants?.phases?.find((phase) => phase.id === phaseEnum)?.name || '' - ); - }; + const getLocalizedPhase = useCallback( + (phaseEnum: PhaseEnum | null | undefined): string => { + return ( + constants?.phases?.find((phase) => phase.id === phaseEnum)?.name || '' + ); + }, + [constants], + ); return { getLocalizedContactStatus, diff --git a/src/utils/functions/convertContactStatus.ts b/src/utils/functions/convertContactStatus.ts index 76620711e..c69d5353e 100644 --- a/src/utils/functions/convertContactStatus.ts +++ b/src/utils/functions/convertContactStatus.ts @@ -6,10 +6,9 @@ import { StatusEnum } from 'src/graphql/types.generated'; export const convertStatus = ( status: string | null | undefined, ): StatusEnum | null => { - const foundStatus = - Object.values(StatusEnum).find((value) => { - return String(value.toLowerCase()) === String(status?.toLowerCase()); - }) || null; + const foundStatus = Object.values(StatusEnum).find( + (value) => value.toLowerCase() === status?.toLowerCase(), + ); if (foundStatus) { return foundStatus; @@ -53,7 +52,7 @@ export const findOldStatus = ( case 'Not Interested': return StatusEnum.NotInterested; - case 'UNRESPONSIVE': + case 'Unresponsive': return StatusEnum.Unresponsive; case 'Never Ask': From 48ee1a841b34ac6fcc8164437368e9877c0cfaad Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 18:02:23 -0700 Subject: [PATCH 08/13] Refactor tests --- .../tools/import/csv.page.test.tsx | 10 ---- src/components/Constants/LoadConstantsMock.ts | 16 +++--- .../Constants/UseApiConstants.test.tsx | 5 +- .../PartnershipInfo/PartnershipInfo.test.tsx | 52 ++++++++++--------- .../PartnershipInfo/PartnershipInfo.tsx | 14 ++--- .../ContactTaskRow/ContactTaskRow.test.tsx | 18 +++++++ .../FourteenMonthReports/useCsvData.test.ts | 15 ------ .../Form/LogForm/TaskModalLogForm.test.tsx | 3 -- src/hooks/usePhaseData.test.ts | 20 +------ 9 files changed, 65 insertions(+), 88 deletions(-) diff --git a/pages/accountLists/[accountListId]/tools/import/csv.page.test.tsx b/pages/accountLists/[accountListId]/tools/import/csv.page.test.tsx index 227fd854a..d267394e4 100644 --- a/pages/accountLists/[accountListId]/tools/import/csv.page.test.tsx +++ b/pages/accountLists/[accountListId]/tools/import/csv.page.test.tsx @@ -3,7 +3,6 @@ import { ThemeProvider } from '@mui/material/styles'; import { render, waitFor } from '@testing-library/react'; import TestRouter from '__tests__/util/TestRouter'; import TestWrapper from '__tests__/util/TestWrapper'; -import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { CsvImportViewStepEnum } from 'src/components/Tool/Import/Csv/CsvImportContext'; import { get } from 'src/components/Tool/Import/Csv/csvImportService'; import { useAccountListId } from 'src/hooks/useAccountListId'; @@ -11,19 +10,11 @@ import theme from 'src/theme'; import CsvHome from './csv.page'; jest.mock('src/hooks/useAccountListId'); -jest.mock('src/components/Constants/UseApiConstants'); jest.mock('src/components/Tool/Import/Csv/csvImportService'); const accountListId = 'accountListId'; const csvFileId = 'csvFileId'; -const constants = { - sendAppeals: [ - { id: true, value: 'Yes' }, - { id: false, value: 'No' }, - ], -}; - const buildRouter = (tab) => { return { isReady: true, @@ -49,7 +40,6 @@ const renderCsvHome = (router) => describe('CSV wrapper page', () => { beforeEach(() => { (useAccountListId as jest.Mock).mockReturnValue(accountListId); - (useApiConstants as jest.Mock).mockReturnValue(constants); (get as jest.Mock).mockReturnValue(Promise.resolve({ id: 'from-get' })); }); diff --git a/src/components/Constants/LoadConstantsMock.ts b/src/components/Constants/LoadConstantsMock.ts index f7361d4ee..eb980693b 100644 --- a/src/components/Constants/LoadConstantsMock.ts +++ b/src/components/Constants/LoadConstantsMock.ts @@ -141,27 +141,27 @@ export const loadConstantsMockData: LoadConstantsQuery = { { id: ActivityTypeEnum.FollowUpPhoneCall, name: 'phone call to follow up', - value: 'Follow Up - Phone Call', + value: 'Follow-Up - Phone Call', }, { id: ActivityTypeEnum.FollowUpEmail, name: 'email to follow up', - value: 'Follow Up - Email', + value: 'Follow-Up - Email', }, { id: ActivityTypeEnum.FollowUpTextMessage, name: 'text message to follow up', - value: 'Follow Up - Text Message', + value: 'Follow-Up - Text Message', }, { id: ActivityTypeEnum.FollowUpSocialMedia, name: 'social media message to follow up', - value: 'Follow Up - Social Media', + value: 'Follow-Up - Social Media', }, { id: ActivityTypeEnum.FollowUpInPerson, name: 'follow up in person', - value: 'Follow Up - In Person', + value: 'Follow-Up - In Person', }, { id: ActivityTypeEnum.PartnerCarePhoneCall, @@ -191,7 +191,7 @@ export const loadConstantsMockData: LoadConstantsQuery = { { id: ActivityTypeEnum.PartnerCareThank, name: 'send thank you note', - value: 'Partner Care - Thank', + value: 'Partner Care - Thank You Note', }, { id: ActivityTypeEnum.PartnerCareDigitalNewsletter, @@ -901,7 +901,11 @@ export const loadConstantsMockData: LoadConstantsQuery = { { code: 'EUR', codeSymbolString: 'EUR (€)', + id: 'EUR', + key: 'EUR', name: 'Euro', + symbol: '€', + value: 'EUR (€)', }, ], pledgeFrequency: [ diff --git a/src/components/Constants/UseApiConstants.test.tsx b/src/components/Constants/UseApiConstants.test.tsx index 0398d9a85..2ddaef91a 100644 --- a/src/components/Constants/UseApiConstants.test.tsx +++ b/src/components/Constants/UseApiConstants.test.tsx @@ -2,11 +2,14 @@ import { renderHook } from '@testing-library/react-hooks'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { useApiConstants } from './UseApiConstants'; +jest.unmock('src/components/Constants/UseApiConstants'); + describe('LoadConstants', () => { it('returns an object', async () => { - const { result } = renderHook(() => useApiConstants(), { + const { result, waitForNextUpdate } = renderHook(() => useApiConstants(), { wrapper: GqlMockedProvider, }); + await waitForNextUpdate(); expect(result.current?.activities).toBeTruthy(); expect(result.current?.languages).toBeTruthy(); diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.test.tsx b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.test.tsx index d170652e2..fe0e807a5 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.test.tsx @@ -8,7 +8,6 @@ import { DateTime } from 'luxon'; import { SnackbarProvider } from 'notistack'; import { gqlMock } from '__tests__/util/graphqlMocking'; import { render, waitFor } from '__tests__/util/testingLibraryReactMock'; -import { LoadConstantsDocument } from 'src/components/Constants/LoadConstants.generated'; import { PledgeFrequencyEnum, StatusEnum } from 'src/graphql/types.generated'; import theme from '../../../../../theme'; import { @@ -37,6 +36,15 @@ const mock = gqlMock( }, ); +const emptyMock = gqlMock( + ContactDonorAccountsFragmentDoc, + { + mocks: { + status: null, + }, + }, +); + jest.mock('next/router', () => ({ useRouter: () => { return { @@ -48,31 +56,11 @@ jest.mock('next/router', () => ({ describe('PartnershipInfo', () => { it('test renderer', async () => { - const { getByText } = render( + const { getByText, findByText } = render( - + @@ -82,8 +70,24 @@ describe('PartnershipInfo', () => { await waitFor(() => { expect(getByText('CA$55 - Annual')).toBeInTheDocument(); - expect(getByText('Principal - Financial')).toBeInTheDocument(); }); + expect(await findByText('Partner - Financial')).toBeInTheDocument(); + }); + + it('renders No Status', async () => { + const { findByText } = render( + + + + + + + + + , + ); + + expect(await findByText('No Status')).toBeInTheDocument(); }); it('should open edit partnership information modal', () => { diff --git a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.tsx b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.tsx index 51945cfcf..6e583a25a 100644 --- a/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.tsx +++ b/src/components/Contacts/ContactDetails/ContactDonationsTab/PartnershipInfo/PartnershipInfo.tsx @@ -8,8 +8,8 @@ import { Box, IconButton, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; -import { useLoadConstantsQuery } from 'src/components/Constants/LoadConstants.generated'; import { useLocale } from 'src/hooks/useLocale'; +import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; import { dateFormat } from 'src/lib/intlFormat'; import { getLocalizedPledgeFrequency } from 'src/utils/functions/getLocalizedPledgeFrequency'; import { sourceToStr } from 'src/utils/sourceHelper'; @@ -69,15 +69,7 @@ interface PartnershipInfoProp { export const PartnershipInfo: React.FC = ({ contact }) => { const { t } = useTranslation(); const locale = useLocale(); - const { data } = useLoadConstantsQuery(); - const constants = data?.constant; - const [status, setStatus] = React.useState( - constants?.status?.find(({ id }) => id === contact?.status), - ); - - React.useEffect(() => { - setStatus(constants?.status?.find(({ id }) => id === contact?.status)); - }, [data?.constant]); + const { getLocalizedContactStatus } = useLocalizedConstants(); const [editPartnershipModalOpen, setEditPartnershipModalOpen] = useState(false); @@ -100,7 +92,7 @@ export const PartnershipInfo: React.FC = ({ contact }) => { - {contact?.status ? status?.value : t('No Status')} + {getLocalizedContactStatus(contact?.status) || t('No Status')} {`${currencyFormat( diff --git a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx index d5232c456..fcce4e74e 100644 --- a/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactTasksTab/ContactTaskRow/ContactTaskRow.test.tsx @@ -3,6 +3,7 @@ import { ThemeProvider } from '@mui/material/styles'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { GqlMockedProvider, gqlMock } from '__tests__/util/graphqlMocking'; +import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; import { TaskModalEnum } from 'src/components/Task/Modal/TaskModal'; import { ResultEnum } from 'src/graphql/types.generated'; import useTaskModal from '../../../../../hooks/useTaskModal'; @@ -152,4 +153,21 @@ describe('ContactTaskRow', () => { ); }); }); + + describe('activity type', () => { + it.each(loadConstantsMockData.constant.activities || [])( + 'displays $value', + (activity) => { + const task = gqlMock(TaskRowFragmentDoc, { + mocks: { + activityType: activity?.id, + }, + }); + + const { getByText } = render(); + + expect(getByText(activity?.value)).toBeVisible(); + }, + ); + }); }); diff --git a/src/components/Reports/FourteenMonthReports/useCsvData.test.ts b/src/components/Reports/FourteenMonthReports/useCsvData.test.ts index 8d77faa11..72b4397fe 100644 --- a/src/components/Reports/FourteenMonthReports/useCsvData.test.ts +++ b/src/components/Reports/FourteenMonthReports/useCsvData.test.ts @@ -2,12 +2,6 @@ import { renderHook } from '@testing-library/react-hooks'; import { ErgonoMockShape } from 'graphql-ergonomock'; import { DeepPartial } from 'ts-essentials'; import { gqlMock } from '__tests__/util/graphqlMocking'; -import { - LoadConstantsDocument, - LoadConstantsQuery, -} from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; -import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { StatusEnum } from 'src/graphql/types.generated'; import { CurrencyTable } from './FourteenMonthReport'; import { @@ -16,15 +10,6 @@ import { } from './GetFourteenMonthReport.generated'; import { useCsvData } from './useCsvData'; -jest.mock('src/components/Constants/UseApiConstants.tsx'); - -// Mock useApiConstants to make the data available synchronously instead of having to wait for the GraphQL call -(useApiConstants as jest.MockedFn).mockReturnValue( - gqlMock(LoadConstantsDocument, { - mocks: loadConstantsMockData, - }).constant, -); - const mockContact = ( mocks?: ErgonoMockShape & DeepPartial, ) => diff --git a/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx b/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx index 64cdf66b8..0078f88aa 100644 --- a/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx +++ b/src/components/Task/Modal/Form/LogForm/TaskModalLogForm.test.tsx @@ -184,9 +184,6 @@ describe('TaskModalLogForm', () => { expect(getByLabelText('Tags')).toBeInTheDocument(); expect(getByLabelText('Assignee')).toBeInTheDocument(); - await waitFor(() => { - expect(getByText('Save')).not.toBeDisabled(); - }); userEvent.click(getByText('Save')); await waitFor(() => expect(onClose).toHaveBeenCalled()); }, 25000); diff --git a/src/hooks/usePhaseData.test.ts b/src/hooks/usePhaseData.test.ts index 0133627b1..0e52b51e5 100644 --- a/src/hooks/usePhaseData.test.ts +++ b/src/hooks/usePhaseData.test.ts @@ -1,23 +1,7 @@ import { renderHook } from '@testing-library/react-hooks'; -import { gqlMock } from '__tests__/util/graphqlMocking'; -import { - LoadConstantsDocument, - LoadConstantsQuery, -} from 'src/components/Constants/LoadConstants.generated'; -import { loadConstantsMockData } from 'src/components/Constants/LoadConstantsMock'; -import { useApiConstants } from 'src/components/Constants/UseApiConstants'; import { PhaseEnum } from 'src/graphql/types.generated'; import { usePhaseData } from 'src/hooks/usePhaseData'; -jest.mock('src/components/Constants/UseApiConstants.tsx'); - -// Mock useApiConstants to make the data available synchronously instead of having to wait for the GraphQL call -(useApiConstants as jest.MockedFn).mockReturnValue( - gqlMock(LoadConstantsDocument, { - mocks: loadConstantsMockData, - }).constant, -); - describe('usePhaseData', () => { it('should return correctly formatted phaseData', () => { const { result } = renderHook(() => usePhaseData(PhaseEnum.FollowUp)); @@ -104,7 +88,7 @@ describe('usePhaseData', () => { name: 'Text Message', phase: 'Follow-Up', phaseId: 'FOLLOW_UP', - title: 'Follow Up - Text Message', + title: 'Follow-Up - Text Message', }, INITIATION_EMAIL: { name: 'Email', @@ -116,7 +100,7 @@ describe('usePhaseData', () => { name: 'Social Media', phase: 'Follow-Up', phaseId: 'FOLLOW_UP', - title: 'Follow Up - Social Media', + title: 'Follow-Up - Social Media', }, APPOINTMENT_VIDEO_CALL: { name: 'Video Call', From 0bfc3ad2b99aa03c372b4d16955b8a4bf4698333 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Fri, 18 Oct 2024 20:19:15 -0700 Subject: [PATCH 09/13] Add tests --- .../functions/convertContactStatus.test.ts | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/utils/functions/convertContactStatus.test.ts b/src/utils/functions/convertContactStatus.test.ts index 9440e7551..0074f95d4 100644 --- a/src/utils/functions/convertContactStatus.test.ts +++ b/src/utils/functions/convertContactStatus.test.ts @@ -3,10 +3,6 @@ import { convertStatus } from './convertContactStatus'; describe('convertContactStatus', () => { it('converts to StatusEnum or null', () => { - // old status - expect(convertStatus('Partner - Financial')).toEqual( - StatusEnum.PartnerFinancial, - ); // new lowercase status expect(convertStatus('partner_financial')).toEqual( StatusEnum.PartnerFinancial, @@ -20,4 +16,28 @@ describe('convertContactStatus', () => { expect(convertStatus(undefined)).toEqual(null); expect(convertStatus('')).toEqual(null); }); + + it('converts old status format to StatusEnum or null', () => { + expect(convertStatus('Partner - Financial')).toEqual( + StatusEnum.PartnerFinancial, + ); + expect(convertStatus('Partner - Special')).toEqual( + StatusEnum.PartnerSpecial, + ); + expect(convertStatus('Call for Decision')).toEqual( + StatusEnum.CallForDecision, + ); + expect(convertStatus('Expired Referral')).toEqual( + StatusEnum.ExpiredReferral, + ); + expect(convertStatus('Cultivate Relationship')).toEqual( + StatusEnum.CultivateRelationship, + ); + expect(convertStatus('Partner - Pray')).toEqual(StatusEnum.PartnerPray); + expect(convertStatus('Ask in Future')).toEqual(StatusEnum.AskInFuture); + expect(convertStatus('Not Interested')).toEqual(StatusEnum.NotInterested); + expect(convertStatus('Never Contacted')).toEqual(StatusEnum.NeverContacted); + expect(convertStatus('Unresponsive')).toEqual(StatusEnum.Unresponsive); + expect(convertStatus('Never Ask')).toEqual(StatusEnum.NeverAsk); + }); }); From 3102911a4a84881335f2552a1986d71c55d507f4 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Mon, 21 Oct 2024 09:01:26 -0700 Subject: [PATCH 10/13] Refetch constants when the language changes --- src/components/Constants/LoadConstants.graphql | 7 +++++++ src/components/Constants/UseApiConstants.tsx | 17 ++++++++++++++++- .../LanguageAccordion/LanguageAccordion.tsx | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/components/Constants/LoadConstants.graphql b/src/components/Constants/LoadConstants.graphql index af1dc610b..89504ed68 100644 --- a/src/components/Constants/LoadConstants.graphql +++ b/src/components/Constants/LoadConstants.graphql @@ -83,4 +83,11 @@ query LoadConstants { tasks } } + user { + id + preferences { + id + locale + } + } } diff --git a/src/components/Constants/UseApiConstants.tsx b/src/components/Constants/UseApiConstants.tsx index 1785b2bf9..6c7bc265c 100644 --- a/src/components/Constants/UseApiConstants.tsx +++ b/src/components/Constants/UseApiConstants.tsx @@ -1,3 +1,5 @@ +import { useEffect } from 'react'; +import { useLocalStorage } from 'src/hooks/useLocalStorage'; import { LoadConstantsQuery, useLoadConstantsQuery, @@ -7,9 +9,22 @@ import { export const useApiConstants = (): | LoadConstantsQuery['constant'] | undefined => { - const { data } = useLoadConstantsQuery({ + const { data, refetch } = useLoadConstantsQuery({ fetchPolicy: 'cache-first', }); + const userSavedLanguage = data?.user.preferences?.locale; + const [localeStorageLanguage, setLocaleStorageLanguage] = useLocalStorage( + `user-language`, + '', + ); + useEffect(() => { + // if the language in locale storage is different than the saved language, the user may have changed languages in a different browser. If so, refetch the constants. + if (localeStorageLanguage !== userSavedLanguage) { + setLocaleStorageLanguage(userSavedLanguage || ''); + refetch(); + } + }, [userSavedLanguage]); + return data?.constant; }; diff --git a/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx b/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx index a9c1b48e2..2e84b7647 100644 --- a/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx +++ b/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx @@ -9,6 +9,7 @@ import { FieldWrapper } from 'src/components/Shared/Forms/FieldWrapper'; import { FormWrapper } from 'src/components/Shared/Forms/FormWrapper'; import { Preference } from 'src/graphql/types.generated'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; +import { useLocalStorage } from 'src/hooks/useLocalStorage'; import { formatLanguage, languages } from 'src/lib/data/languages'; import { useUpdatePersonalPreferencesMutation } from '../UpdatePersonalPreferences.generated'; @@ -33,6 +34,7 @@ export const LanguageAccordion: React.FC = ({ const { appName } = useGetAppSettings(); const { enqueueSnackbar } = useSnackbar(); const [updatePersonalPreferences] = useUpdatePersonalPreferencesMutation(); + const [_, setLocaleStorageLanguage] = useLocalStorage(`user-language`, ''); const label = t('Language'); @@ -55,6 +57,7 @@ export const LanguageAccordion: React.FC = ({ enqueueSnackbar(t('Saved successfully.'), { variant: 'success', }); + setLocaleStorageLanguage(attributes.locale || ''); }, onError: () => { enqueueSnackbar(t('Saving failed.'), { From 9ce5b2b0490c7eb16c6c0cd2a0b41ae87710a008 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Mon, 21 Oct 2024 10:51:00 -0700 Subject: [PATCH 11/13] Fix constants mocks --- src/components/Constants/LoadConstants.graphql | 2 ++ src/components/Constants/LoadConstantsMock.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/components/Constants/LoadConstants.graphql b/src/components/Constants/LoadConstants.graphql index 89504ed68..43ca73c1a 100644 --- a/src/components/Constants/LoadConstants.graphql +++ b/src/components/Constants/LoadConstants.graphql @@ -31,9 +31,11 @@ query LoadConstants { # } pledgeCurrency { id + key codeSymbolString name code + value symbol } pledgeFrequency { diff --git a/src/components/Constants/LoadConstantsMock.ts b/src/components/Constants/LoadConstantsMock.ts index eb980693b..754f95a10 100644 --- a/src/components/Constants/LoadConstantsMock.ts +++ b/src/components/Constants/LoadConstantsMock.ts @@ -24,6 +24,7 @@ const LoadConstantsMock = (): MockedResponse => { }; export const loadConstantsMockData: LoadConstantsQuery = { + user: { id: '123', preferences: { id: '123', locale: 'en' } }, constant: { status: [ { From b5c1e7d4a0a1290b491734f57171de1f46936dda Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Mon, 21 Oct 2024 12:36:54 -0700 Subject: [PATCH 12/13] Add check before refetching and remove saving to local storage in preferences --- src/components/Constants/UseApiConstants.tsx | 11 ++++++----- .../LanguageAccordion/LanguageAccordion.tsx | 3 --- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Constants/UseApiConstants.tsx b/src/components/Constants/UseApiConstants.tsx index 6c7bc265c..63eade3e0 100644 --- a/src/components/Constants/UseApiConstants.tsx +++ b/src/components/Constants/UseApiConstants.tsx @@ -14,14 +14,15 @@ export const useApiConstants = (): }); const userSavedLanguage = data?.user.preferences?.locale; - const [localeStorageLanguage, setLocaleStorageLanguage] = useLocalStorage( - `user-language`, + const [localStorageLanguage, setLocalStorageLanguage] = useLocalStorage( + `constants-language`, '', ); useEffect(() => { - // if the language in locale storage is different than the saved language, the user may have changed languages in a different browser. If so, refetch the constants. - if (localeStorageLanguage !== userSavedLanguage) { - setLocaleStorageLanguage(userSavedLanguage || ''); + // if the language in local storage is different than the saved language, that may mean the cached constants are in the previous language. If so, refetch the constants. + + if (userSavedLanguage && localStorageLanguage !== userSavedLanguage) { + setLocalStorageLanguage(userSavedLanguage || ''); refetch(); } }, [userSavedLanguage]); diff --git a/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx b/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx index 2e84b7647..a9c1b48e2 100644 --- a/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx +++ b/src/components/Settings/preferences/accordions/LanguageAccordion/LanguageAccordion.tsx @@ -9,7 +9,6 @@ import { FieldWrapper } from 'src/components/Shared/Forms/FieldWrapper'; import { FormWrapper } from 'src/components/Shared/Forms/FormWrapper'; import { Preference } from 'src/graphql/types.generated'; import useGetAppSettings from 'src/hooks/useGetAppSettings'; -import { useLocalStorage } from 'src/hooks/useLocalStorage'; import { formatLanguage, languages } from 'src/lib/data/languages'; import { useUpdatePersonalPreferencesMutation } from '../UpdatePersonalPreferences.generated'; @@ -34,7 +33,6 @@ export const LanguageAccordion: React.FC = ({ const { appName } = useGetAppSettings(); const { enqueueSnackbar } = useSnackbar(); const [updatePersonalPreferences] = useUpdatePersonalPreferencesMutation(); - const [_, setLocaleStorageLanguage] = useLocalStorage(`user-language`, ''); const label = t('Language'); @@ -57,7 +55,6 @@ export const LanguageAccordion: React.FC = ({ enqueueSnackbar(t('Saved successfully.'), { variant: 'success', }); - setLocaleStorageLanguage(attributes.locale || ''); }, onError: () => { enqueueSnackbar(t('Saving failed.'), { From 84f4f4eb607f6f74b4bf0c4e8acbd63281617859 Mon Sep 17 00:00:00 2001 From: Caleb Alldrin Date: Mon, 21 Oct 2024 13:40:59 -0700 Subject: [PATCH 13/13] update useLocalStorage --- src/components/Constants/UseApiConstants.tsx | 8 ++++---- src/hooks/useLocalStorage.ts | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/Constants/UseApiConstants.tsx b/src/components/Constants/UseApiConstants.tsx index 63eade3e0..3d2bf553c 100644 --- a/src/components/Constants/UseApiConstants.tsx +++ b/src/components/Constants/UseApiConstants.tsx @@ -13,7 +13,7 @@ export const useApiConstants = (): fetchPolicy: 'cache-first', }); - const userSavedLanguage = data?.user.preferences?.locale; + const currentLanguage = data?.user.preferences?.locale; const [localStorageLanguage, setLocalStorageLanguage] = useLocalStorage( `constants-language`, '', @@ -21,11 +21,11 @@ export const useApiConstants = (): useEffect(() => { // if the language in local storage is different than the saved language, that may mean the cached constants are in the previous language. If so, refetch the constants. - if (userSavedLanguage && localStorageLanguage !== userSavedLanguage) { - setLocalStorageLanguage(userSavedLanguage || ''); + if (currentLanguage && localStorageLanguage !== currentLanguage) { + setLocalStorageLanguage(currentLanguage); refetch(); } - }, [userSavedLanguage]); + }, [currentLanguage]); return data?.constant; }; diff --git a/src/hooks/useLocalStorage.ts b/src/hooks/useLocalStorage.ts index fef0e7fc1..eaf2e2dd5 100644 --- a/src/hooks/useLocalStorage.ts +++ b/src/hooks/useLocalStorage.ts @@ -6,7 +6,7 @@ const IS_SERVER = typeof window === 'undefined'; export const useLocalStorage = ( key: string, - initialValue: T, + defaultValue: T, ): [T, Dispatch>] => { const deserializer = useCallback<(value: string) => T>( (value) => { @@ -14,31 +14,31 @@ export const useLocalStorage = ( try { parsed = JSON.parse(value); } catch (error) { - return initialValue; // Return initialValue if parsing fails + return defaultValue; // Return defaultValue if parsing fails } return parsed as T; }, - [initialValue], + [defaultValue], ); // Get from local storage then - // parse stored json or return initialValue + // parse stored json or return defaultValue const readValue = useCallback((): T => { // Prevent build error "window is undefined" but keeps working if (IS_SERVER) { - return initialValue; + return defaultValue; } try { const raw = window.localStorage.getItem(key); - return raw ? deserializer(raw) : initialValue; + return raw ? deserializer(raw) : defaultValue; } catch (error) { - return initialValue; + return defaultValue; } - }, [initialValue, key, deserializer]); + }, [defaultValue, key, deserializer]); - const [storedValue, setStoredValue] = useState(initialValue); + const [storedValue, setStoredValue] = useState(readValue()); // Return a wrapped version of useState's setter function that ... // ... persists the new value to localStorage.