Skip to content

Commit

Permalink
Add text customization to porting embed (#9)
Browse files Browse the repository at this point in the history
- Adds `options.text` to specify custom text
- Create default text and merge it into custom text
- Use text options everywhere
- Documentation
  • Loading branch information
timomeh committed Mar 5, 2024
1 parent 77c4437 commit cce2cd9
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 30 deletions.
4 changes: 2 additions & 2 deletions docs/README.md
@@ -1,4 +1,4 @@
# Docs

- [README](README.md)
- [Porting Embed](docs/porting-embed.md)
- [README](../README.md)
- [Porting Embed](./porting-embed.md)
53 changes: 52 additions & 1 deletion docs/porting-embed.md
Expand Up @@ -54,6 +54,7 @@ embed.on('completed', ({ porting }) => {
- [Show UI depending on the current step](#show-ui-depending-on-the-current-step)
- [Use the included styling](#use-the-included-styling)
- [CSS customization](#css-customization)
- [Translations and text customization](#translations-and-text-customization)
- [Continue after all fields were filled out](#continue-after-all-fields-were-filled-out)
- [Show a loading state while the form is submitted](#show-a-loading-state-while-the-form-is-submitted)
- [Reacting to validation changes](#reacting-to-validation-changes)
Expand All @@ -77,6 +78,7 @@ embed.on('completed', ({ porting }) => {
- [Values](#values)
- [Step](#step)
- [Field Names](#field-names)
- [Text](#text)

## Usage

Expand Down Expand Up @@ -156,6 +158,25 @@ See also:
- [Step](#step) for the list of steps.
- [Field Names](#field-names) for the list of field names.

### Translations and text customization

The embed ships with default text in english, but you can override any text that is rendered by the embed, if you want to support a different language or customize the text otherwise.

```js
const embed = await PortingEmbed(session, {
options: {
text: {
'field.firstName.label': 'Vorname',
'field.firstName.error.required': 'Muss ausgefüllt werden',
'field.lastName.label': 'Nachname',
'field.lastName.error.required': 'Muss ausgefüllt werden',
'field.birthday.label': 'Geburtsdatum',
// ...
}
}
})
```

### Continue after all fields were filled out

When the user entered all fields in all steps of the wizard, the embed will not render any UI anymore. You can handle this in your app by listening for the `completed` event.
Expand Down Expand Up @@ -456,4 +477,34 @@ Each field in the a step has a name. If a field is not required by a porting, it
- `state` (text)
- `country` (text)
- Form: `donorProviderApproval`
- `donorProviderApproval` (checkbox)
- `donorProviderApproval` (checkbox)

#### Text

| Key | Default |
| - | - |
| `field.accountNumber.label` | Account Number |
| `field.accountNumber.error.required` | The account number is required |
| `field.accountPin.label` | Account PIN |
| `field.accountPin.error.required` | The account pin is required |
| `field.accountPin.error.cleared` | The new account pin is empty. If you do not want to change the account pin, clear the input. |
| `field.firstName.label` | First Name |
| `field.firstName.error.required` | Your first name is required |
| `field.lastName.label` | Last Name |
| `field.lastName.error.required` | Your last name is required |
| `field.birthday.label` | Birthday |
| `field.birthday.error.required` | Your birthday is required |
| `field.line1.label` | Line 1 |
| `field.line1.error.required` | Line 1 is required |
| `field.line2.label` | Line 2 |
| `field.city.label` | City |
| `field.city.error.required` | City is required |
| `field.postalCode.label` | Postal Code |
| `field.postalCode.error.required` | Postal Code is required |
| `field.state.label` | State (ISO code) |
| `field.state.error.format` | Must be an ISO state code |
| `field.country.label` | Country (2 letter code) |
| `field.country.error.required` | Country is required |
| `field.country.error.format` | Must be an ISO country code |
| `field.donorProviderApproval.label` | I have notified my current provider of the number porting and got the approval that the number can be ported |
| `field.donorProviderApproval.error.required` | You must get the approval of your current provider |
40 changes: 39 additions & 1 deletion lib/PortingEmbed/Options.tsx
Expand Up @@ -18,6 +18,34 @@ type FormState = {

export const defaultFormId = 'gigsPortingEmbedForm'

const defaultText = {
'field.accountNumber.label': `Account Number`,
'field.accountNumber.error.required': `The account number is required`,
'field.accountPin.label': `Account PIN`,
'field.accountPin.error.required': `The account pin is required`,
'field.accountPin.error.cleared': `The new account pin is empty. If you do not want to change the account pin, clear the input.`,
'field.firstName.label': `First Name`,
'field.firstName.error.required': `Your first name is required`,
'field.lastName.label': `Last Name`,
'field.lastName.error.required': `Your last name is required`,
'field.birthday.label': `Birthday`,
'field.birthday.error.required': `Your birthday is required`,
'field.line1.label': `Line 1`,
'field.line1.error.required': `Line 1 is required`,
'field.line2.label': `Line 2`,
'field.city.label': `City`,
'field.city.error.required': `City is required`,
'field.postalCode.label': `Postal Code`,
'field.postalCode.error.required': `Postal Code is required`,
'field.state.label': `State (ISO code)`,
'field.state.error.format': `Must be an ISO state code`,
'field.country.label': `Country (2 letter code)`,
'field.country.error.required': `Country is required`,
'field.country.error.format': `Must be an ISO country code`,
'field.donorProviderApproval.label': `I have notified my current provider of the number porting and got the approval that the number can be ported`,
'field.donorProviderApproval.error.required': `You must get the approval of your current provider`,
}

export type EmbedOptions = {
formId?: string
className?: {
Expand All @@ -27,10 +55,20 @@ export type EmbedOptions = {
label?: (state: FieldState) => string
error?: (state: Omit<FieldState, 'valid'>) => string
}
text?: Partial<typeof defaultText>
}

export const OptionsContext = createContext<EmbedOptions>({})

export function useEmbedOptions() {
return useContext(OptionsContext)
const options = useContext(OptionsContext)
const mergedOptions = {
...options,
text: {
...defaultText,
...options.text,
},
}

return mergedOptions
}
34 changes: 22 additions & 12 deletions lib/PortingEmbed/StepAddressForm.tsx
Expand Up @@ -80,12 +80,14 @@ export function StepAddressForm({
>
<Field
name="line1"
validate={[required('Line 1 is required')]}
validate={[required(options.text['field.line1.error.required'])]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Line 1</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.line1.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -100,7 +102,9 @@ export function StepAddressForm({
<Field name="line2" transform={toTrimmed({ on: 'input' })}>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Line 2</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.line2.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -113,12 +117,14 @@ export function StepAddressForm({
</Field>
<Field
name="city"
validate={[required('City is required')]}
validate={[required(options.text['field.city.error.required'])]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>City</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.city.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -132,12 +138,14 @@ export function StepAddressForm({
</Field>
<Field
name="postalCode"
validate={[required('Postal Code is required')]}
validate={[required(options.text['field.postalCode.error.required'])]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Postal Code</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.postalCode.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -153,13 +161,15 @@ export function StepAddressForm({
name="state"
validate={pattern(
/^[A-Z]{1,3}(-[A-Z0-9]{1,3})?$/,
'Must be an ISO state code',
options.text['field.state.error.format'],
)}
transform={[toTrimmed({ on: 'input' }), toUpperCase({ on: 'input' })]}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>State (ISO code)</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.state.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -174,15 +184,15 @@ export function StepAddressForm({
<Field
name="country"
validate={[
required('Country is required'),
pattern(/^[A-Z]{2}$/, 'Must be an iso country code'),
required(options.text['field.country.error.required']),
pattern(/^[A-Z]{2}$/, options.text['field.country.error.format']),
]}
transform={[toTrimmed({ on: 'input' }), toUpperCase({ on: 'input' })]}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>
Country (2 letter code)
{options.text['field.country.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
Expand Down
16 changes: 11 additions & 5 deletions lib/PortingEmbed/StepCarrierDetailsForm.tsx
Expand Up @@ -72,7 +72,7 @@ export function StepCarrierDetailsForm({
setError(
form,
'accountPin',
'The new account pin is empty. If you do not want to change the account pin, clear the input.',
options.text['field.accountPin.error.cleared'],
)
return
}
Expand All @@ -87,12 +87,16 @@ export function StepCarrierDetailsForm({
{porting.required.includes('accountNumber') && (
<Field
name="accountNumber"
validate={[required('The account number is required')]}
validate={[
required(options.text['field.accountNumber.error.required']),
]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Account Number</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.accountNumber.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -111,12 +115,14 @@ export function StepCarrierDetailsForm({
validate={
porting.accountPinExists
? []
: [required('The account pin is required')]
: [required(options.text['field.accountPin.error.required'])]
}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Account PIN</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.accountPin.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand Down
5 changes: 2 additions & 3 deletions lib/PortingEmbed/StepDonorProviderApprovalForm.tsx
Expand Up @@ -61,7 +61,7 @@ export function StepDonorProviderApprovalForm({
name="donorProviderApproval"
type="boolean"
validate={[
required('You must get the approval of your current provider'),
required(options.text['field.donorProviderApproval.error.required']),
]}
>
{(field, props) => (
Expand All @@ -74,8 +74,7 @@ export function StepDonorProviderApprovalForm({
checked={field.value}
/>
<EmbedFieldLabel of={field}>
I have notified my current provider of the number porting and
got the approval that the number can be ported
{options.text['field.donorProviderApproval.label']}
</EmbedFieldLabel>
</div>
<EmbedFieldError of={field} />
Expand Down
18 changes: 12 additions & 6 deletions lib/PortingEmbed/StepHolderDetailsForm.tsx
Expand Up @@ -64,12 +64,14 @@ export function StepHolderDetailsForm({
{porting.required.includes('firstName') && (
<Field
name="firstName"
validate={[required('Your first name is required')]}
validate={[required(options.text['field.firstName.error.required'])]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>First Name</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.firstName.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -85,12 +87,14 @@ export function StepHolderDetailsForm({
{porting.required.includes('lastName') && (
<Field
name="lastName"
validate={[required('Your last name is required')]}
validate={[required(options.text['field.lastName.error.required'])]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Last Name</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.lastName.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand All @@ -106,12 +110,14 @@ export function StepHolderDetailsForm({
{porting.required.includes('birthday') && (
<Field
name="birthday"
validate={[required('Your birthday is required')]}
validate={[required(options.text['field.birthday.error.required'])]}
transform={toTrimmed({ on: 'input' })}
>
{(field, props) => (
<EmbedField of={field}>
<EmbedFieldLabel of={field}>Birthday</EmbedFieldLabel>
<EmbedFieldLabel of={field}>
{options.text['field.birthday.label']}
</EmbedFieldLabel>
<EmbedFieldInput
{...props}
of={field}
Expand Down
31 changes: 31 additions & 0 deletions lib/PortingEmbed/__tests__/StepAddressForm.test.tsx
Expand Up @@ -580,3 +580,34 @@ describe('form class names', () => {
expect(screen.getByRole('form')).toHaveClass('custom-class-submitting')
})
})

describe('custom labels', () => {
it('allows to specify custom labels', () => {
const porting = portingFactory.build({ required: ['address'] })
render(
<OptionsContext.Provider
value={{
text: {
'field.line1.label': 'L1',
'field.line2.label': 'L2',
'field.city.label': 'CI',
'field.postalCode.label': 'PC',
'field.state.label': 'ST',
'field.country.label': 'CO',
},
}}
>
<StepAddressForm porting={porting} onSubmit={vi.fn()} />
</OptionsContext.Provider>,
{
wrapper,
},
)
expect(screen.getByLabelText('L1')).toBeInTheDocument()
expect(screen.getByLabelText('L2')).toBeInTheDocument()
expect(screen.getByLabelText('CI')).toBeInTheDocument()
expect(screen.getByLabelText('PC')).toBeInTheDocument()
expect(screen.getByLabelText('ST')).toBeInTheDocument()
expect(screen.getByLabelText('CO')).toBeInTheDocument()
})
})

0 comments on commit cce2cd9

Please sign in to comment.