diff --git a/.eslintrc.js b/.eslintrc.js index e9c9826d58..94648b7cda 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -95,6 +95,12 @@ module.exports = { message: 'Do not pass i18nKey to . `yarn extract` writes the id as its own value, so the English never reaches translation.json. Drop i18nKey and let the children be the key.', }, + { + selector: + "JSXOpeningElement[name.name='Trans']:not(:has(JSXAttribute[name.name='t'][parent.name.name='Trans']))", + message: + ' must be passed a t={t} prop from useTranslation() so it resolves keys against the component i18n instance.', + }, ], 'react/jsx-no-useless-fragment': 'error', 'react/prop-types': 'off', diff --git a/__tests__/eslintrules/eslintTransRules.test.ts b/__tests__/eslintrules/eslintTransRules.test.ts index 0b5a202bdc..d47f1ec218 100644 --- a/__tests__/eslintrules/eslintTransRules.test.ts +++ b/__tests__/eslintrules/eslintTransRules.test.ts @@ -1,6 +1,7 @@ import { lintSnippet, ruleFor } from './restrictedSyntaxHarness'; const i18nKeyRule = ruleFor("JSXAttribute[name.name='i18nKey']"); +const missingTRule = ruleFor("[parent.name.name='Trans']"); const lintTrans = (body: string): string[] => lintSnippet(`export const Probe = ({ name, t }) => (\n ${body}\n);`).map( @@ -48,3 +49,69 @@ describe(' i18nKey no-restricted-syntax rule', () => { ).toEqual([i18nKeyRule.message]); }); }); + +describe(' t prop no-restricted-syntax rule', () => { + it('flags a with no t prop', () => { + expect(lintTrans('All set')).toEqual([missingTRule.message]); + }); + + it('flags a self-closing with no t prop', () => { + expect( + lintTrans(''), + ).toEqual([missingTRule.message]); + }); + + it('flags a nested with no t prop', () => { + expect( + lintTrans('Hello there'), + ).toEqual([missingTRule.message]); + }); + + it('flags a whose only t prop is on a nested element in an attribute value', () => { + expect( + lintTrans(' }}>Hello'), + ).toEqual([missingTRule.message]); + }); + + it('flags a passed as another component prop', () => { + expect( + lintTrans('Are you sure?} />'), + ).toEqual([missingTRule.message]); + }); + + it('flags every that is missing t, not just the first', () => { + expect( + lintTrans( + '<>OneTwoThree', + ), + ).toEqual([missingTRule.message, missingTRule.message]); + }); + + it('accepts a that is passed t', () => { + expect(lintTrans('All set')).toEqual([]); + }); + + it('accepts a t prop bound to a differently named function', () => { + expect(lintTrans('All set')).toEqual([]); + }); + + it('accepts a t prop bound to a member expression', () => { + expect(lintTrans('All set')).toEqual([]); + }); + + it('accepts a passed as another component prop with t', () => { + expect( + lintTrans( + 'Are you sure?} />', + ), + ).toEqual([]); + }); + + it('accepts a component that is not with no t prop', () => { + expect(lintTrans('All set')).toEqual([]); + }); + + it('accepts a component whose name merely starts with Trans', () => { + expect(lintTrans('All set')).toEqual([]); + }); +}); diff --git a/pages/accountLists/[accountListId]/setup/finish.page.tsx b/pages/accountLists/[accountListId]/setup/finish.page.tsx index 5abbd42129..b03748e8c0 100644 --- a/pages/accountLists/[accountListId]/setup/finish.page.tsx +++ b/pages/accountLists/[accountListId]/setup/finish.page.tsx @@ -2,7 +2,7 @@ import Head from 'next/head'; import { useRouter } from 'next/router'; import React, { useEffect } from 'react'; import { Button } from '@mui/material'; -import { Trans, useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import { ensureSessionAndAccountList } from 'pages/api/utils/pagePropsHelpers'; import { SetupPage } from 'src/components/Setup/SetupPage'; import { LargeButton } from 'src/components/Setup/styledComponents'; @@ -43,11 +43,11 @@ const FinishPage: React.FC = () => { + <> {t('Congratulations!')}
{t("You're all set!")} -
+ } >

diff --git a/src/components/HrTools/MinisterHousingAllowance/MainPages/EligibleDisplay.tsx b/src/components/HrTools/MinisterHousingAllowance/MainPages/EligibleDisplay.tsx index 3c2911e932..9f7b4a9bf5 100644 --- a/src/components/HrTools/MinisterHousingAllowance/MainPages/EligibleDisplay.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/MainPages/EligibleDisplay.tsx @@ -1,5 +1,5 @@ import { Box } from '@mui/material'; -import { Trans } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; export interface EligibleDisplayProps { isPending: boolean; @@ -10,24 +10,26 @@ export const EligibleDisplay: React.FC = ({ isPending, isEditable, }) => { + const { t } = useTranslation(); + return ( {isPending ? (

- + Our records indicate that you have an MHA request{' '} waiting to be processed. To view your MHA request, click on the "View Current MHA" button below. {isEditable && ( - + If you would like to make changes to your request, click on the "Edit Request" button below. )}

) : ( - +

Our records indicate that you have an approved MHA amount. To view your MHA amount, click on the "View Current MHA" button diff --git a/src/components/HrTools/MinisterHousingAllowance/Steps/StepOne/AboutForm.tsx b/src/components/HrTools/MinisterHousingAllowance/Steps/StepOne/AboutForm.tsx index 7d7a4427eb..bd5a848ce3 100644 --- a/src/components/HrTools/MinisterHousingAllowance/Steps/StepOne/AboutForm.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/Steps/StepOne/AboutForm.tsx @@ -57,7 +57,7 @@ export const AboutForm: React.FC = ({ {t('About this Form')}

- + A Minister's Housing Allowance Request is a form ministers complete to designate part of their compensation as tax-free housing allowance. To complete this form for the {{ nextYear }} tax year, @@ -90,7 +90,7 @@ export const AboutForm: React.FC = ({ - + The next time the board will approve MHA Requests is {after} and your approved annual MHA amount will appear on your{' '} diff --git a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.tsx b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.tsx index 892d703064..8b5edd63c1 100644 --- a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/FairRentalValue.tsx @@ -36,7 +36,7 @@ export const FairRentalValue: React.FC = ({ schema }) => { {t('Monthly market rental value of your home.')} - + The best way to determine this amount is to have an appraiser or rental real estate specialist provide you with a written estimate of the monthly rental value. If this is not possible, @@ -72,7 +72,7 @@ export const FairRentalValue: React.FC = ({ schema }) => { )} - + This is a reasonable amount by which the monthly rental of your home would increase if it were furnished. diff --git a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/RequestSummaryCard.tsx b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/RequestSummaryCard.tsx index f5e0cdba0c..686a3c7f1d 100644 --- a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/RequestSummaryCard.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/CalcComponents/RequestSummaryCard.tsx @@ -85,7 +85,7 @@ export const RequestSummaryCard: React.FC = ({ {t('Your Annual MHA Total')} - + This is calculated from your {above} responses and is the lower of the Annual Fair Rental Value or the Annual Cost of Providing a Home. diff --git a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx index a6d22123e1..50a6db06ab 100644 --- a/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx +++ b/src/components/HrTools/MinisterHousingAllowance/Steps/StepThree/Calculation.tsx @@ -278,7 +278,7 @@ export const Calculation: React.FC = ({ ) : actionRequired ? (

- + Please review the Annual MHA Request that you have submitted for Board approval and make any changes necessary here. The board will review this {{ after }} and you will receive notice @@ -287,7 +287,7 @@ export const Calculation: React.FC = ({

) : (

- + Please enter dollar amounts for each category below to calculate your Annual MHA. The board will review this{' '} {{ after }} and you will receive notice of your {{ approval }} diff --git a/src/components/HrTools/SalaryCalculator/EffectiveDateStep/EffectiveDateBanner/EffectiveDateBanner.tsx b/src/components/HrTools/SalaryCalculator/EffectiveDateStep/EffectiveDateBanner/EffectiveDateBanner.tsx index fd2c873d7d..d6835b0c7b 100644 --- a/src/components/HrTools/SalaryCalculator/EffectiveDateStep/EffectiveDateBanner/EffectiveDateBanner.tsx +++ b/src/components/HrTools/SalaryCalculator/EffectiveDateStep/EffectiveDateBanner/EffectiveDateBanner.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Alert, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; -import { Trans } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { navBarHeight } from 'src/components/Layouts/Primary/Primary'; const StyledAlert = styled(Alert)({ @@ -25,6 +25,7 @@ interface EffectiveDateBannerProps { export const EffectiveDateBanner: React.FC = ({ onClose, }) => { + const { t } = useTranslation(); const thisYear = DateTime.now().year; const nextYear = thisYear + 1; @@ -36,7 +37,7 @@ export const EffectiveDateBanner: React.FC = ({ data-testid="effective-date-banner-text" > - + Dates for {'{{nextYear}}'} are unavailable at this time while we update salary level tables. By December 15, {'{{thisYear}}'} you will be able to request a salary change for {'{{nextYear}}'}. diff --git a/src/components/Settings/integrations/Google/GoogleAccordion.tsx b/src/components/Settings/integrations/Google/GoogleAccordion.tsx index b711bbd5f2..fb4c344619 100644 --- a/src/components/Settings/integrations/Google/GoogleAccordion.tsx +++ b/src/components/Settings/integrations/Google/GoogleAccordion.tsx @@ -236,6 +236,7 @@ export const GoogleAccordion: React.FC = ({ color={theme.palette.mpdxGrayDark.main} > = ({ page }) => { + const { t } = useTranslation(); const { openTaskModal, preloadTaskModal } = useTaskModal(); const [contactsDialogOpen, setContactsDialogOpen] = useState(false); @@ -51,7 +52,7 @@ const CreateButton: React.FC = ({ page }) => { backgroundColor: theme.palette.mpdxBlue.main, }} > - + {renderDialog( AddMenuItemsEnum.NewContact, @@ -104,19 +105,21 @@ const NullState: React.FC = ({ <> diff --git a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx index 19e13d376c..2cb6752c0a 100644 --- a/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx +++ b/src/components/Task/Modal/Form/Inputs/SuggestedContactStatus/SuggestedContactStatus.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { Checkbox, FormControl, FormControlLabel, Grid } from '@mui/material'; -import { Trans } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; import { PhaseEnum, StatusEnum } from 'src/graphql/types.generated'; import { useContactPartnershipStatuses } from 'src/hooks/useContactPartnershipStatuses'; import { useLocalizedConstants } from 'src/hooks/useLocalizedConstants'; @@ -35,6 +35,7 @@ export const SuggestedContactStatus: React.FC = ({ if (!contactIds || contactIds.length !== 1) { return null; } + const { t } = useTranslation(); const contactId = contactIds[0]; const { data } = useContactStatusQuery({ variables: { @@ -80,6 +81,7 @@ export const SuggestedContactStatus: React.FC = ({ } label={ = ({ accountListId }) => { = ({ } else if (pledge && totalSelectedDonationsAmount < pledge.amount) { setLessThanPledgeConfirmationMessage( }} />, diff --git a/src/components/Tool/FixCommitmentInfo/FixCommitmentInfo.tsx b/src/components/Tool/FixCommitmentInfo/FixCommitmentInfo.tsx index e5541fbf87..c41b201011 100644 --- a/src/components/Tool/FixCommitmentInfo/FixCommitmentInfo.tsx +++ b/src/components/Tool/FixCommitmentInfo/FixCommitmentInfo.tsx @@ -288,6 +288,7 @@ const FixCommitmentInfo: React.FC = ({ accountListId }: Props) => { title={modalState.title} message={ = ({ = ({ accountListId }: Props) => { = ({ accountListId }: Props) => { }} /> diff --git a/src/components/Tool/FixSendNewsletter/FixSendNewsletter.tsx b/src/components/Tool/FixSendNewsletter/FixSendNewsletter.tsx index ce8dabb2fd..987d4866eb 100644 --- a/src/components/Tool/FixSendNewsletter/FixSendNewsletter.tsx +++ b/src/components/Tool/FixSendNewsletter/FixSendNewsletter.tsx @@ -138,6 +138,7 @@ const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { { = ({ accountListId }: Props) => { = ({ accountListId }: Props) => { > { = ({ accountListId }: Props) => { }} title={ = ({ {isContactType && ( = ({ = ({ = ({ > = ({ = ({ accountListId }: Props) => { >