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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ module.exports = {
message:
'Do not pass i18nKey to <Trans>. `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:
'<Trans> 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',
Expand Down
67 changes: 67 additions & 0 deletions __tests__/eslintrules/eslintTransRules.test.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand Down Expand Up @@ -48,3 +49,69 @@ describe('<Trans> i18nKey no-restricted-syntax rule', () => {
).toEqual([i18nKeyRule.message]);
});
});

describe('<Trans> t prop no-restricted-syntax rule', () => {
it('flags a <Trans> with no t prop', () => {
expect(lintTrans('<Trans>All set</Trans>')).toEqual([missingTRule.message]);
});

it('flags a self-closing <Trans> with no t prop', () => {
expect(
lintTrans('<Trans defaults="Hello {{ name }}" values={{ name }} />'),
).toEqual([missingTRule.message]);
});

it('flags a nested <Trans> with no t prop', () => {
expect(
lintTrans('<Trans t={t}>Hello <Trans>there</Trans></Trans>'),
Comment thread
zweatshirt marked this conversation as resolved.
).toEqual([missingTRule.message]);
});

it('flags a <Trans> whose only t prop is on a nested element in an attribute value', () => {
expect(
lintTrans('<Trans components={{ bold: <Link t={t} /> }}>Hello</Trans>'),
).toEqual([missingTRule.message]);
});

it('flags a <Trans> passed as another component prop', () => {
expect(
lintTrans('<Confirmation message={<Trans>Are you sure?</Trans>} />'),
).toEqual([missingTRule.message]);
});

it('flags every <Trans> that is missing t, not just the first', () => {
expect(
lintTrans(
'<><Trans>One</Trans><Trans t={t}>Two</Trans><Trans>Three</Trans></>',
),
).toEqual([missingTRule.message, missingTRule.message]);
});

it('accepts a <Trans> that is passed t', () => {
expect(lintTrans('<Trans t={t}>All set</Trans>')).toEqual([]);
});

it('accepts a t prop bound to a differently named function', () => {
expect(lintTrans('<Trans t={translate}>All set</Trans>')).toEqual([]);
});

it('accepts a t prop bound to a member expression', () => {
expect(lintTrans('<Trans t={i18n.t}>All set</Trans>')).toEqual([]);
});

it('accepts a <Trans> passed as another component prop with t', () => {
expect(
lintTrans(
'<Confirmation message={<Trans t={t}>Are you sure?</Trans>} />',
),
).toEqual([]);
});

it('accepts a component that is not <Trans> with no t prop', () => {
expect(lintTrans('<Typography>All set</Typography>')).toEqual([]);
});

it('accepts a component whose name merely starts with Trans', () => {
expect(lintTrans('<Transition>All set</Transition>')).toEqual([]);
});
});
6 changes: 3 additions & 3 deletions pages/accountLists/[accountListId]/setup/finish.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -43,11 +43,11 @@ const FinishPage: React.FC = () => {
</Head>
<SetupPage
title={
<Trans>
<>
Comment thread
zweatshirt marked this conversation as resolved.
{t('Congratulations!')}
<br />
{t("You're all set!")}
</Trans>
</>
}
>
<p>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,24 +10,26 @@ export const EligibleDisplay: React.FC<EligibleDisplayProps> = ({
isPending,
isEditable,
}) => {
const { t } = useTranslation();

return (
<Box>
{isPending ? (
<p style={{ lineHeight: 1.5 }}>
<Trans>
<Trans t={t}>
Our records indicate that you have an MHA request{' '}
<strong>waiting to be processed</strong>. To view your MHA request,
click on the &quot;View Current MHA&quot; button below.
</Trans>
{isEditable && (
<Trans>
<Trans t={t}>
If you would like to make changes to your request, click on the
&quot;Edit Request&quot; button below.
</Trans>
)}
</p>
) : (
<Trans>
<Trans t={t}>
<p style={{ lineHeight: 1.5 }}>
Our records indicate that you have an approved MHA amount. To view
your MHA amount, click on the &quot;View Current MHA&quot; button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const AboutForm: React.FC<AboutFormProps> = ({
<Typography variant="h5">{t('About this Form')}</Typography>
</Box>
<p style={{ lineHeight: 1.5 }}>
<Trans>
<Trans t={t}>
A Minister&apos;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,
Expand Down Expand Up @@ -90,7 +90,7 @@ export const AboutForm: React.FC<AboutFormProps> = ({
</StyledListItem>
</List>
</Box>
<Trans values={{ boardDateFormatted, availableDateFormatted }}>
<Trans t={t} values={{ boardDateFormatted, availableDateFormatted }}>
<Box sx={{ mt: 2 }}>
The next time the board will approve MHA Requests is {after} and your
approved annual MHA amount will appear on your{' '}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const FairRentalValue: React.FC<FairRentalValueProps> = ({ schema }) => {
{t('Monthly market rental value of your home.')}
</Typography>
<Box sx={{ color: 'text.secondary' }}>
<Trans>
<Trans t={t}>
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,
Expand Down Expand Up @@ -72,7 +72,7 @@ export const FairRentalValue: React.FC<FairRentalValueProps> = ({ schema }) => {
)}
</Typography>
<Box sx={{ color: 'text.secondary' }}>
<Trans>
<Trans t={t}>
This is a reasonable amount by which the monthly rental of your
home would increase if it were furnished.
</Trans>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const RequestSummaryCard: React.FC<RequestSummaryCardProps> = ({
<b>{t('Your Annual MHA Total')}</b>
</Typography>
<Box sx={{ color: 'text.secondary' }}>
<Trans values={{ above }}>
<Trans t={t} values={{ above }}>
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export const Calculation: React.FC<CalculationProps> = ({
<PersonInfo />
) : actionRequired ? (
<p style={{ lineHeight: 1.5 }}>
<Trans values={{ after, approval }}>
<Trans t={t} values={{ after, approval }}>
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
Expand All @@ -287,7 +287,7 @@ export const Calculation: React.FC<CalculationProps> = ({
</p>
) : (
<p style={{ lineHeight: 1.5 }}>
<Trans values={{ after, approval }}>
<Trans t={t} values={{ after, approval }}>
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 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)({
Expand All @@ -25,6 +25,7 @@ interface EffectiveDateBannerProps {
export const EffectiveDateBanner: React.FC<EffectiveDateBannerProps> = ({
onClose,
}) => {
const { t } = useTranslation();
const thisYear = DateTime.now().year;
const nextYear = thisYear + 1;

Expand All @@ -36,7 +37,7 @@ export const EffectiveDateBanner: React.FC<EffectiveDateBannerProps> = ({
data-testid="effective-date-banner-text"
>
<Typography fontWeight="bold" textAlign="center">
<Trans values={{ thisYear, nextYear }}>
<Trans t={t} values={{ thisYear, nextYear }}>
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}}'}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@
color={theme.palette.mpdxGrayDark.main}
>
<Trans
t={t}

Check warning on line 239 in src/components/Settings/integrations/Google/GoogleAccordion.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Large Method

GoogleAccordion:React.FC<AccordionProps> increases from 190 to 191 lines of code, threshold = 120 Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
defaults="When you add a Google account to {{appName}}, Google will ask you what {{appName}} should be allowed to access. <bold>Please select ALL of the checkboxes.</bold><br/><br/>Otherwise, {{appName}} may not work properly."
shouldUnescape
values={{ appName }}
Expand Down
7 changes: 5 additions & 2 deletions src/components/Shared/Filters/NullState/NullState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface CreateButtonProps {
}

const CreateButton: React.FC<CreateButtonProps> = ({ page }) => {
const { t } = useTranslation();
const { openTaskModal, preloadTaskModal } = useTaskModal();
const [contactsDialogOpen, setContactsDialogOpen] = useState(false);

Expand Down Expand Up @@ -51,7 +52,7 @@ const CreateButton: React.FC<CreateButtonProps> = ({ page }) => {
backgroundColor: theme.palette.mpdxBlue.main,
}}
>
<Trans defaults="Add new {{page}}" values={{ page }} />
<Trans t={t} defaults="Add new {{page}}" values={{ page }} />
</Button>
{renderDialog(
AddMenuItemsEnum.NewContact,
Expand Down Expand Up @@ -104,19 +105,21 @@ const NullState: React.FC<Props> = ({
<>
<Typography variant="h5">
<Trans
t={t}
defaults="Looks like you haven't added any {{page}}s yet"
values={{ page }}
/>
</Typography>
<Typography>
<Trans
t={t}
defaults="You can import {{page}}s from another service or add a new {{page}}."
values={{ page }}
/>
</Typography>
<Box display="flex" mt={1}>
<Button variant="contained">
<Trans defaults="Import {{page}}s" values={{ page }} />
<Trans t={t} defaults="Import {{page}}s" values={{ page }} />
</Button>
<CreateButton page={page} />
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -35,6 +35,7 @@ export const SuggestedContactStatus: React.FC<SuggestedContactStatusProps> = ({
if (!contactIds || contactIds.length !== 1) {
return null;
}
const { t } = useTranslation();
const contactId = contactIds[0];
const { data } = useContactStatusQuery({
variables: {
Expand Down Expand Up @@ -80,6 +81,7 @@ export const SuggestedContactStatus: React.FC<SuggestedContactStatusProps> = ({
}
label={
<Trans
t={t}
defaults="Change the contact's status to: <bold>{{status}}</bold>" // optional defaultValue
values={{
status: getLocalizedContactStatus(suggestedContactStatus),
Expand Down
1 change: 1 addition & 0 deletions src/components/Tool/Appeal/InitialPage/Appeals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const Appeals: React.FC<AppealsProps> = ({ accountListId }) => {
<Box display="flex" justifyContent="center">
<Typography data-testid="TypographyShowing">
<Trans
t={t}
defaults="Showing <bold>{{value}}</bold> of <bold>{{total}}</bold>"
shouldUnescape
values={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export const UpdateDonationsModal: React.FC<UpdateDonationsModalProps> = ({
} else if (pledge && totalSelectedDonationsAmount < pledge.amount) {
setLessThanPledgeConfirmationMessage(
<Trans
t={t}
defaults="The total amount is less than the commitment amount. Would you like to update the commitment amount to match the total? If not, the contact will be moved to the <bold>Received</bold> column."
components={{ bold: <strong /> }}
/>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ const FixCommitmentInfo: React.FC<Props> = ({ accountListId }: Props) => {
title={modalState.title}
message={
<Trans
t={t}
defaults="{{message}}"
shouldUnescape
values={{ message: modalState.message }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ export const FixEmailAddresses: React.FC<FixEmailAddressesProps> = ({
<Box width="100%" display="flex" justifyContent="center">
<Typography>
<Trans
t={t}
defaults="Showing <bold>{{value}}</bold> of <bold>{{total}}</bold>"
shouldUnescape
values={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@
<Box className={classes.footer}>
<Typography>
<Trans
t={t}

Check warning on line 462 in src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Complex Method

FixMailingAddresses:React.FC<Props> already has high cyclomatic complexity, and now it increases in Lines of Code from 357 to 358 This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
defaults="Showing <bold>{{value}}</bold> of <bold>{{value}}</bold>"
shouldUnescape
values={{ value: totalContacts }}
Expand Down
1 change: 1 addition & 0 deletions src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
<Box className={classes.footer}>
<Typography>
<Trans
t={t}

Check warning on line 202 in src/components/Tool/FixPhoneNumbers/FixPhoneNumbers.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Large Method

FixPhoneNumbers:React.FC<Props> increases from 143 to 144 lines of code, threshold = 120 Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
defaults="Showing <bold>{{value}}</bold> of <bold>{{value}}</bold>"
shouldUnescape
values={{ value: data.people.totalCount }}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Tool/FixSendNewsletter/Contact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ const Contact = ({
{contact.primaryAddress?.source && (
<Typography variant="body2">
<Trans
t={t}
defaults="<bold>Source:</bold> {{where}} ({{date}})"
shouldUnescape
values={{
Expand Down Expand Up @@ -288,6 +289,7 @@ const Contact = ({
>
<Typography variant="body2">
<Trans
t={t}
defaults="<bold>Send newsletter?</bold>"
components={{ bold: <strong /> }}
/>
Expand Down
3 changes: 3 additions & 0 deletions src/components/Tool/FixSendNewsletter/FixSendNewsletter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ const FixSendNewsletter: React.FC<Props> = ({ accountListId }: Props) => {
<strong>
{
<Trans
t={t}
defaults="You have {{amount}} newsletter statuses to confirm."
values={{
amount: loading ? '...' : totalCount,
Expand All @@ -159,6 +160,7 @@ const FixSendNewsletter: React.FC<Props> = ({ accountListId }: Props) => {
<Box>
<Typography>
<Trans
t={t}
defaults="<i>Showing <bold>{{numberOfContacts}}</bold> of <bold>{{totalCount}}</bold></i>"
shouldUnescape
values={{
Expand All @@ -179,6 +181,7 @@ const FixSendNewsletter: React.FC<Props> = ({ accountListId }: Props) => {
>
{
<Trans
t={t}
defaults="Confirm All ({{value}})"
values={{
value: numberOfContactsShowing,
Expand Down
Loading
Loading