Skip to content

Commit

Permalink
fix: stub i18next for YesNo, Email fields
Browse files Browse the repository at this point in the history
  • Loading branch information
LoneRifle committed Dec 16, 2024
1 parent 9be50f2 commit c8775df
Show file tree
Hide file tree
Showing 23 changed files with 162 additions and 58 deletions.
25 changes: 3 additions & 22 deletions frontend/src/components/Field/YesNo/YesNo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,12 @@ import {
} from '@chakra-ui/react'
import pick from 'lodash/pick'

import { Language } from '~shared/types'

import { FieldColorScheme } from '~theme/foundations/colours'

import { YesNoOption } from './YesNoOption'

export type YesNoOptionValue = 'Yes' | 'No'

// TODO: port to i18next
type YesNoTranslations = {
Yes: string
No: string
}

const YES_NO_TRANSLATIONS: Record<Language, YesNoTranslations> = {
[Language.ENGLISH]: { Yes: 'Yes', No: 'No' },
[Language.CHINESE]: { Yes: '是', No: '否' },
[Language.MALAY]: { Yes: 'Ya', No: 'Tidak' },
[Language.TAMIL]: { Yes: 'ஆம்', No: 'இல்லை' },
}

export interface YesNoProps {
/**
* Whether YesNo component is disabled.
Expand Down Expand Up @@ -68,7 +53,7 @@ export const YesNo = forwardRef<YesNoProps, 'input'>(
({ colorScheme, ...props }, ref) => {
const formControlProps = useFormControlProps(props)
const { getRootProps, getRadioProps, onChange } = useRadioGroup(props)
const { t, i18n } = useTranslation()
const { t } = useTranslation()

const groupProps = getRootProps()
const [noProps, yesProps] = useMemo(() => {
Expand All @@ -95,10 +80,6 @@ export const YesNo = forwardRef<YesNoProps, 'input'>(
return [noRadioProps, yesRadioProps]
}, [formControlProps, getRadioProps, props.name])

const selectedLanguage = i18n.language as Language
const yesLabel = YES_NO_TRANSLATIONS[selectedLanguage].Yes
const noLabel = YES_NO_TRANSLATIONS[selectedLanguage].No

return (
<HStack spacing={0} {...groupProps}>
<YesNoOption
Expand All @@ -107,7 +88,7 @@ export const YesNo = forwardRef<YesNoProps, 'input'>(
{...noProps}
onChange={(value) => onChange(value as YesNoOptionValue)}
leftIcon={BiX}
label={noLabel ?? t('features.adminForm.sidebar.fields.yesNo.no')}
label={t('features.adminForm.sidebar.fields.yesNo.no')}
// Ref is set here for tracking current value, and also so any errors
// can focus this input.
ref={ref}
Expand All @@ -119,7 +100,7 @@ export const YesNo = forwardRef<YesNoProps, 'input'>(
{...yesProps}
onChange={(value) => onChange(value as YesNoOptionValue)}
leftIcon={BiCheck}
label={yesLabel ?? t('features.adminForm.sidebar.fields.yesNo.yes')}
label={t('features.adminForm.sidebar.fields.yesNo.yes')}
title={props.title}
/>
</HStack>
Expand Down
11 changes: 0 additions & 11 deletions frontend/src/constants/validation.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
import { Language } from '~shared/types'

export const REQUIRED_ERROR = 'This field is required'

export const INVALID_EMAIL_ERROR = 'Please enter a valid email'
export const INVALID_EMAIL_DOMAIN_ERROR: Record<Language, string> = {
[Language.ENGLISH]:
'The entered email does not belong to an allowed email domain',
[Language.CHINESE]: '输入的电子邮箱不在允许域名之列',
[Language.MALAY]:
'E-mel yang dimasukkan bukan milik domain e-mel yang dibenarkan',
[Language.TAMIL]:
'உள்ளிடப்பட்ட மின்னஞ்சல் அனுமதிக்கப்பட்ட மின்னஞ்சலுக்குச் சொந்தமானதல்ல',
}

export const INVALID_DROPDOWN_OPTION_ERROR =
'Entered value is not a valid dropdown option'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Box, VisuallyHidden } from '@chakra-ui/react'

import { Language } from '~shared/types'

import { baseEmailValidationFn } from '~utils/fieldValidation'
import { EmailFieldInput, EmailFieldProps } from '~templates/Field/Email'
import { EmailFieldSchema } from '~templates/Field/types'
Expand Down Expand Up @@ -71,10 +69,13 @@ export const VerifiableEmailField = ({
schema,
...props
}: VerifiableEmailFieldProps) => {
const { i18n } = useTranslation()
const { t } = useTranslation()
const validateInputForVfn = baseEmailValidationFn({
schema,
selectedLanguage: i18n.language as Language,
validationErrorMessages: t(
'features.adminForm.sidebar.fields.email.validation',
{ allowObjects: true },
),
})
return (
<VerifiableFieldProvider
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/i18n/locales/features/admin-form/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './en-sg'
export { type Meta } from './meta'
export { type Modals } from './modals'
export * from './ms-sg'
export { type Navbar } from './navbar'
export { type Settings } from './settings'
export {
Expand All @@ -9,4 +10,6 @@ export {
type Logic,
type ThankYou,
} from './sidebar'
export * from './ta-sg'
export { type Toasts } from './toasts'
export * from './zh-sg'
5 changes: 5 additions & 0 deletions frontend/src/i18n/locales/features/admin-form/ms-sg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { msSG as sidebar } from './sidebar'

export const msSG = {
sidebar,
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ export const enSG: Fields = {
includePdfResponseWarning:
'PDF responses are not available for payment forms.',
},
validation: {
domainDisallowed:
'The entered email does not belong to an allowed email domain',
},
},
mobileNo: {
otpVerification: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ export interface Fields {
includePdfResponse: string
includePdfResponseWarning: string
}
validation: {
domainDisallowed: string
}
}
mobileNo: {
otpVerification: {
Expand Down Expand Up @@ -180,3 +183,7 @@ export interface Fields {
}
}
}

export * from './ms-sg'
export * from './ta-sg'
export * from './zh-sg'
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { DeepPartial } from 'ts-essentials'

import { Fields } from '.'

export const msSG: DeepPartial<Fields> = {
yesNo: {
yes: 'Ya',
no: 'Tidak',
},
email: {
validation: {
domainDisallowed:
'E-mel yang dimasukkan bukan milik domain e-mel yang dibenarkan',
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { DeepPartial } from 'ts-essentials'

import { Fields } from '.'

export const taSG: DeepPartial<Fields> = {
yesNo: {
yes: 'ஆம்',
no: 'இல்லை',
},
email: {
validation: {
domainDisallowed:
'உள்ளிடப்பட்ட மின்னஞ்சல் அனுமதிக்கப்பட்ட மின்னஞ்சலுக்குச் சொந்தமானதல்ல',
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DeepPartial } from 'ts-essentials'

import { Fields } from '.'

export const zhSG: DeepPartial<Fields> = {
yesNo: {
yes: '是',
no: '否',
},
email: {
validation: {
domainDisallowed: '输入的电子邮箱不在允许域名之列',
},
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ export * from './en-sg'
export { type Fields } from './fields'
export { type HeaderAndInstructions } from './header-and-instructions'
export { type Logic } from './logic'
export * from './ms-sg'
export * from './ta-sg'
export { type ThankYou } from './thank-you'
export * from './zh-sg'
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { msSG as fields } from './fields'

export const msSG = {
fields,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { taSG as fields } from './fields'

export const taSG = {
fields,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { zhSG as fields } from './fields'

export const zhSG = {
fields,
}
5 changes: 5 additions & 0 deletions frontend/src/i18n/locales/features/admin-form/ta-sg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { taSG as sidebar } from './sidebar'

export const taSG = {
sidebar,
}
5 changes: 5 additions & 0 deletions frontend/src/i18n/locales/features/admin-form/zh-sg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { zhSG as sidebar } from './sidebar'

export const zhSG = {
sidebar,
}
10 changes: 8 additions & 2 deletions frontend/src/i18n/locales/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { ResourceLanguage } from 'i18next'

import { Language } from '~shared/types'

import { enSG } from './en-sg'
import { msSG } from './ms-sg'
import { taSG } from './ta-sg'
import { zhSG } from './zh-sg'

export const locales = {
'en-SG': enSG as unknown as ResourceLanguage,
'zh-SG': zhSG as unknown as ResourceLanguage,
[Language.ENGLISH]: enSG as unknown as ResourceLanguage,
[Language.CHINESE]: zhSG as unknown as ResourceLanguage,
[Language.MALAY]: msSG as unknown as ResourceLanguage,
[Language.TAMIL]: taSG as unknown as ResourceLanguage,
}
12 changes: 12 additions & 0 deletions frontend/src/i18n/locales/ms-sg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PartialDeep } from 'type-fest'

import { msSG as adminForm } from './features/admin-form'
import Translation from './types'

export const msSG: PartialDeep<Translation> = {
translation: {
features: {
adminForm,
},
},
}
12 changes: 12 additions & 0 deletions frontend/src/i18n/locales/ta-sg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PartialDeep } from 'type-fest'

import { taSG as adminForm } from './features/admin-form'
import Translation from './types'

export const taSG: PartialDeep<Translation> = {
translation: {
features: {
adminForm,
},
},
}
2 changes: 2 additions & 0 deletions frontend/src/i18n/locales/zh-sg.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { PartialDeep } from 'type-fest'

import { zhSG as adminForm } from './features/admin-form'
import { zhSG as login } from './features/login'
import Translation from './types'

export const zhSG: PartialDeep<Translation> = {
translation: {
features: {
adminForm,
login,
},
},
Expand Down
10 changes: 3 additions & 7 deletions frontend/src/templates/Field/Email/EmailField.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import { composeStories } from '@storybook/react'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import { Language } from '~shared/types'
import { enSG } from '~/i18n/locales/features/admin-form/sidebar/fields'

import {
INVALID_EMAIL_DOMAIN_ERROR,
INVALID_EMAIL_ERROR,
REQUIRED_ERROR,
} from '~constants/validation'
import { INVALID_EMAIL_ERROR, REQUIRED_ERROR } from '~constants/validation'

import * as stories from './EmailField.stories'

Expand Down Expand Up @@ -149,7 +145,7 @@ describe('email validation', () => {

// Assert
// Should show error message.
const errorMessage = INVALID_EMAIL_DOMAIN_ERROR[Language.ENGLISH]
const errorMessage = enSG.email.validation.domainDisallowed
expect(screen.getByText(errorMessage)).not.toBeNull()
})
})
11 changes: 7 additions & 4 deletions frontend/src/templates/Field/Email/EmailFieldInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,19 @@ export const EmailFieldInput = ({
handleInputChange,
inputProps = {},
}: EmailFieldInputProps): JSX.Element => {
const { i18n } = useTranslation()
const selectedLanguage = i18n.language as Language
const { t } = useTranslation()
const validationErrorMessages = t(
'features.adminForm.sidebar.fields.email.validation',
{ allowObjects: true },
)
const validationRules = useMemo(
() =>
createEmailValidationRules(
schema,
disableRequiredValidation,
selectedLanguage,
validationErrorMessages,
),
[schema, disableRequiredValidation, selectedLanguage],
[schema, disableRequiredValidation, validationErrorMessages],
)

const { control } = useFormContext<VerifiableFieldInput>()
Expand Down
Loading

0 comments on commit c8775df

Please sign in to comment.