Skip to content

Commit

Permalink
feat(i18n): extract general,notifs,webhooks settings (#7955)
Browse files Browse the repository at this point in the history
Take the trouble to also extract the common 'Loading...' text at
various points in the codebase.
  • Loading branch information
LoneRifle authored Dec 13, 2024
1 parent dd8cd1e commit 56cac09
Show file tree
Hide file tree
Showing 30 changed files with 358 additions and 58 deletions.
8 changes: 2 additions & 6 deletions __tests__/e2e/helpers/createForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,7 @@ const addSettings = async (
await page.getByRole('tab', { name: 'General' }).dispatchEvent('click')

// Ensure that we are on the general settings page
await expect(
page.getByRole('heading', { name: 'General settings' }),
).toBeVisible()
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible()

// Toggle form to be open
await page
Expand Down Expand Up @@ -226,9 +224,7 @@ const addGeneralSettings = async (
await page.getByRole('tab', { name: 'General' }).click()

// Ensure that we are on the general settings page
await expect(
page.getByRole('heading', { name: 'General settings' }),
).toBeVisible()
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible()

await expectToast(page, /your form is closed to new responses/i)

Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/Spinner/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { BiLoader } from 'react-icons/bi'
import {
Flex,
Expand Down Expand Up @@ -47,10 +48,11 @@ const spin = keyframes({
export const Spinner = ({
speed = '2.5s',
color = 'inherit',
label = 'Loading...',
label: userSpecifiedLabel,
fontSize = '1rem',
...flexProps
}: SpinnerProps): JSX.Element => {
const { t } = useTranslation()
const prefersReducedMotion = usePrefersReducedMotion()

const animation = useMemo(
Expand All @@ -59,6 +61,8 @@ export const Spinner = ({
[prefersReducedMotion, speed],
)

const label = userSpecifiedLabel ?? t('features.common.loadingWithEllipsis')

return (
<Flex color={color} align="center" {...flexProps}>
{label && <VisuallyHidden>{label}</VisuallyHidden>}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslation } from 'react-i18next'
import { BiHomeAlt } from 'react-icons/bi'
import { Link as ReactLink } from 'react-router-dom'
import { Icon, Skeleton, Stack, Text } from '@chakra-ui/react'
Expand All @@ -13,6 +14,7 @@ type AdminFormNavbarDetailsProps = Pick<AdminFormNavbarProps, 'formInfo'>
export const AdminFormNavbarBreadcrumbs = ({
formInfo,
}: AdminFormNavbarDetailsProps): JSX.Element => {
const { t } = useTranslation()
const isMobile = useIsMobile()

return (
Expand Down Expand Up @@ -44,7 +46,7 @@ export const AdminFormNavbarBreadcrumbs = ({
overflow="hidden"
color="secondary.500"
>
{formInfo ? formInfo.title : 'Loading...'}
{formInfo ? formInfo.title : t('features.common.loadingWithEllipsis')}
</Text>
</Skeleton>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { memo, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { BiDownload } from 'react-icons/bi'
import { useParams } from 'react-router-dom'
import {
Expand Down Expand Up @@ -76,6 +77,7 @@ const StackRow = ({
}

export const IndividualResponsePage = (): JSX.Element => {
const { t } = useTranslation()
const { submissionId, formId } = useParams()
if (!submissionId) throw new Error('Missing submissionId')
if (!formId) throw new Error('Missing formId')
Expand Down Expand Up @@ -156,7 +158,9 @@ export const IndividualResponsePage = (): JSX.Element => {
/>
<StackRow
label="Timestamp"
value={data?.submissionTime ?? 'Loading...'}
value={
data?.submissionTime ?? t('features.common.loadingWithEllipsis')
}
isLoading={isLoading}
isError={isError}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslation } from 'react-i18next'
import { Skeleton } from '@chakra-ui/react'

import { FormResponseMode, FormSettings, FormStatus } from '~shared/types/form'
Expand Down Expand Up @@ -37,6 +38,7 @@ const FormEmailSectionContainer = ({
}

export const SettingsEmailsPage = (): JSX.Element => {
const { t } = useTranslation()
const { data: settings } = useAdminFormSettings()

const isFormPublic = settings?.status === FormStatus.Public
Expand All @@ -54,7 +56,9 @@ export const SettingsEmailsPage = (): JSX.Element => {

return (
<>
<CategoryHeader>Email notifications</CategoryHeader>
<CategoryHeader>
{t('features.adminForm.settings.emailNotifications.title')}
</CategoryHeader>
{settings ? (
<>
<EmailNotificationsHeader
Expand Down
12 changes: 7 additions & 5 deletions frontend/src/features/admin-form/settings/SettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { BiCodeBlock, BiCog, BiDollar, BiKey, BiMailSend } from 'react-icons/bi'
import { IconType } from 'react-icons/lib'
import { useNavigate, useParams } from 'react-router-dom'
Expand Down Expand Up @@ -34,6 +35,7 @@ interface TabEntry {

export const SettingsPage = (): JSX.Element => {
const { formId, settingsTab } = useParams()
const { t } = useTranslation()

if (!formId) throw new Error('No formId provided')

Expand All @@ -49,32 +51,32 @@ export const SettingsPage = (): JSX.Element => {

const tabConfig: TabEntry[] = [
{
label: 'General',
label: t('features.adminForm.settings.general.title'),
icon: BiCog,
component: SettingsGeneralPage,
path: 'general',
},
{
label: 'Singpass',
label: t('features.adminForm.settings.singpass.title'),
icon: BiKey,
component: SettingsAuthPage,
path: 'singpass',
},
{
label: 'Email notifications',
label: t('features.adminForm.settings.emailNotifications.title'),
icon: BiMailSend,
component: SettingsEmailsPage,
path: 'email-notifications',
showRedDot: true,
},
{
label: 'Webhooks',
label: t('features.adminForm.settings.webhooks.title'),
icon: BiCodeBlock,
component: SettingsWebhooksPage,
path: 'webhooks',
},
{
label: 'Payments',
label: t('features.adminForm.settings.payments.title'),
icon: BiDollar,
component: SettingsPaymentsPage,
path: 'payments',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTranslation } from 'react-i18next'
import { BiBulb } from 'react-icons/bi'
import { Flex, Icon } from '@chakra-ui/react'

Expand Down Expand Up @@ -30,18 +31,24 @@ export const EmailNotificationsHeader = ({
isPaymentsEnabled,
isFormResponseModeEmail,
}: EmailNotificationsHeaderProps) => {
const { t } = useTranslation()
if (isFormPublic) {
return (
<InlineMessage marginBottom="40px">
To change email recipients, close your form to new responses.
{t(
'features.adminForm.settings.emailNotifications.header.closeFormFirst',
)}
</InlineMessage>
)
}

if (isPaymentsEnabled) {
return (
<InlineMessage useMarkdown marginBottom="40px">
{`Email notifications for payment forms are not available in FormSG. You can configure them using [Plumber](${OGP_PLUMBER}).`}
{t(
'features.adminForm.settings.emailNotifications.header.noEmailsForPaymentForms',
{ url: OGP_PLUMBER },
)}
</InlineMessage>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Skeleton } from '@chakra-ui/react'

import Toggle from '~components/Toggle'
Expand All @@ -7,6 +8,7 @@ import { useMutateFormSettings } from '../mutations'
import { useAdminFormSettings } from '../queries'

export const FormCaptchaToggle = (): JSX.Element => {
const { t } = useTranslation()
const { data: settings, isLoading: isLoadingSettings } =
useAdminFormSettings()

Expand All @@ -25,8 +27,9 @@ export const FormCaptchaToggle = (): JSX.Element => {
<Toggle
isLoading={mutateFormCaptcha.isLoading}
isChecked={hasCaptcha}
label="Enable reCAPTCHA"
description="If you expect non-English-speaking respondents, they may have difficulty understanding the reCAPTCHA selection instructions."
{...t('features.adminForm.settings.general.captcha', {
returnObjects: true,
})}
onChange={() => handleToggleCaptcha()}
/>
</Skeleton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useForm,
useFormContext,
} from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { FormControl } from '@chakra-ui/react'
import { get, isEmpty, isEqual } from 'lodash'
import isEmail from 'validator/lib/isEmail'
Expand Down Expand Up @@ -85,6 +86,7 @@ export const FormEmailSection = ({
isDisabled,
settings,
}: EmailFormSectionProps): JSX.Element => {
const { t } = useTranslation()
const initialEmailSet = useMemo(
() => new Set(settings.emails),
[settings.emails],
Expand Down Expand Up @@ -124,7 +126,9 @@ export const FormEmailSection = ({
useMarkdownForDescription
description={DESCRIPTION_TEXT}
>
Notifications for new responses
{t(
'features.adminForm.settings.emailNotifications.section.regular.label',
)}
</FormLabel>
<AdminEmailRecipientsInput onSubmit={handleSubmitEmails} />
<FormErrorMessage>{get(errors, 'emails.message')}</FormErrorMessage>
Expand All @@ -134,7 +138,9 @@ export const FormEmailSection = ({
mt="0.5rem"
opacity={isDisabled ? '0.3' : '1'}
>
Separate multiple email addresses with a comma
{t(
'features.adminForm.settings.emailNotifications.section.regular.description',
)}
</FormLabel.Description>
) : null}
</FormControl>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useCallback, useMemo } from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Skeleton } from '@chakra-ui/react'

import Toggle from '~components/Toggle'
Expand All @@ -7,6 +8,7 @@ import { useMutateFormSettings } from '../mutations'
import { useAdminFormSettings } from '../queries'

export const FormIssueNotificationToggle = (): JSX.Element => {
const { t } = useTranslation()
const { data: settings, isLoading: isLoadingSettings } =
useAdminFormSettings()

Expand All @@ -26,8 +28,9 @@ export const FormIssueNotificationToggle = (): JSX.Element => {
<Toggle
isLoading={mutateFormIssueNotification.isLoading}
isChecked={hasIssueNotification}
label="Receive email notifications for issues reported by respondents"
description="You will receive a maximum of one email per form, per day if there are any issues reported."
{...t('features.adminForm.settings.general.issueNotifications', {
returnObjects: true,
})}
onChange={() => handleToggleIssueNotification()}
/>
</Skeleton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useRef,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { FormControl, Skeleton } from '@chakra-ui/react'

import { FormResponseMode } from '~shared/types'
Expand All @@ -30,6 +31,7 @@ const FormLimitBlock = ({
initialLimit,
currentResponseCount,
}: FormLimitBlockProps): JSX.Element => {
const { t } = useTranslation()
const [value, setValue] = useState(initialLimit)
const [error, setError] = useState<string>()

Expand All @@ -43,13 +45,15 @@ const FormLimitBlock = ({
setValue(nextVal)
if (parseInt(nextVal, 10) <= currentResponseCount) {
setError(
`Submission limit must be greater than current submission count (${currentResponseCount})`,
t('features.adminForm.settings.general.limit.limitLessThanCurrent', {
currentResponseCount,
}),
)
} else if (error) {
setError(undefined)
}
},
[currentResponseCount, error],
[currentResponseCount, error, t],
)

const handleBlur = useCallback(() => {
Expand Down Expand Up @@ -83,10 +87,11 @@ const FormLimitBlock = ({
<FormControl mt="2rem" isInvalid={!!error}>
<FormLabel
isRequired
description="Your form will automatically close once it reaches the set limit. Enable
reCAPTCHA to prevent spam submissions from triggering this limit."
description={t(
'features.adminForm.settings.general.limit.input.description',
)}
>
Maximum number of responses allowed
{t('features.adminForm.settings.general.limit.input.label')}
</FormLabel>
<NumberInput
maxW="16rem"
Expand All @@ -106,6 +111,7 @@ const FormLimitBlock = ({
}

export const FormLimitToggle = (): JSX.Element => {
const { t } = useTranslation()
const { data: settings, isLoading: isLoadingSettings } =
useAdminFormSettings()

Expand Down Expand Up @@ -157,12 +163,12 @@ export const FormLimitToggle = (): JSX.Element => {
isDisabled={isMrf}
isLoading={mutateFormLimit.isLoading}
isChecked={isLimit}
label="Set a response limit"
label={t('features.adminForm.settings.general.limit.label')}
onChange={() => handleToggleLimit()}
/>
{isMrf ? (
<InlineMessage variant="warning" mt="0.5rem">
Response limits cannot be applied for multi-respondent forms.
{t('features.adminForm.settings.general.limit.notForMRF')}
</InlineMessage>
) : null}
{settings && settings?.submissionLimit !== null && (
Expand Down
Loading

0 comments on commit 56cac09

Please sign in to comment.