Skip to content

Commit

Permalink
Merge pull request #32 from daritelska-platforma/generic-form-updates
Browse files Browse the repository at this point in the history
Generic form updates
  • Loading branch information
kachar authored Feb 11, 2021
2 parents cf0ed12 + 6ba986b commit 450fbc3
Show file tree
Hide file tree
Showing 26 changed files with 663 additions and 457 deletions.
8 changes: 1 addition & 7 deletions public/locales/bg/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@
},
"pages": {
"forgotten-password": {
"instructions": "За да смените паролата си, моля въведете вашия email и ще ви изпратим потвърждение с инареукции."
"instructions": "За да смените паролата си, моля въведете вашия email и ще ви изпратим потвърждение с инструкции."
}
},
"validation":{
"email": "Невалиден email",
"required": "Задължително поле",
"password-min": "Паролата трябва да бъде поне {{count}} символа",
"password-match": "Паролата не съвпада"
}
}
10 changes: 10 additions & 0 deletions public/locales/bg/validation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"invalid": "Невалидно поле",
"required": "Задължително поле",
"email": "Невалиден email",
"password-min": "Паролата трябва да бъде поне {{min}} символа",
"password-match": "Паролата не съвпада",
"field-too-short": "Полето трябва да бъде поне {{min}} символа",
"field-too-long": "Полето трябва да бъде най-много {{max}} символа",
"one-of": "Невалидна стойност"
}
6 changes: 0 additions & 6 deletions public/locales/en/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,5 @@
"forgotten-password": {
"instructions": "To reset your password, please type your email address below. We will then send you an email with instructions to follow."
}
},
"validation":{
"email": "Invalid email",
"required": "Required field",
"password-min": "Password should be at least {{count}} characters",
"password-match": "Password doesn't match"
}
}
10 changes: 10 additions & 0 deletions public/locales/en/validation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"invalid": "Field is invalid",
"required": "Required field",
"email": "Invalid email",
"password-min": "Password should be at least {{min}} characters",
"password-match": "Password doesn't match",
"field-too-short": "Field should be at least {{min}} symbols",
"field-too-long": "Field should be maximum {{max}} symbols",
"one-of": "Invalid value"
}
150 changes: 150 additions & 0 deletions src/common/form/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Forms and validation

## Form definition

```tsx
import React from 'react'
import * as yup from 'yup'
import { useTranslation } from 'react-i18next'
import { Grid, TextField, Button } from '@material-ui/core'

import { AlertStore } from 'stores/AlertStore'
import useForm, { translateError, customValidators } from 'common/form/useForm'

export type FormData = {
email: string
}

const validationSchema: yup.SchemaOf<FormData> = yup.object().defined().shape({
email: yup.string().email().required(),
})

const defaults: FormData = {
email: '',
}

export type MyFormProps = { initialValues?: FormData }

export default function MyForm({ initialValues = defaults }: MyFormProps) {
const { t } = useTranslation()

const { formik } = useForm({
initialValues,
validationSchema,
onSubmit: (values) => {
console.log(values)
},
})

return (
<form onSubmit={formik.handleSubmit}>
<Grid container spacing={3}>
<Grid item xs={12}>
<TextField
type="text"
fullWidth
label={t('auth:fields.email')}
name="email"
size="small"
variant="outlined"
autoFocus
error={Boolean(formik.errors.email)}
helperText={translateError(formik.errors.email)}
value={formik.values.email}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
/>
</Grid>
<Grid item xs={12}>
<Button fullWidth type="submit" color="primary" variant="contained">
{t('auth:cta.login')}
</Button>
</Grid>
</Grid>
</form>
)
}
```

## Form usage

```tsx
<MyForm />

<MyForm initailValues={{email: '[email protected]'}} />
```

## Validation

### Default translations

Simple strings are mapped directly to their respective translation

```json
{
"invalid": "Field is invalid",
"required": "Required field"
}
```

```tsx
setLocale({
mixed: {
default: 'validation:invalid',
required: 'validation:required',
},
string: {
email: 'validation:email',
},
})
```

### Default translations with interpolation

Complex translation keys are being evaluated upon translation

```json
{
"field-too-short": "Field should be at least {{min}} symbols",
"field-too-long": "Field should be maximum {{max}} symbols"
}
```

```tsx
setLocale({
string: {
min: ({ min }: { min: number }) => ({
key: 'validation:field-too-short',
values: { min },
}),
max: ({ max }: { max: number }) => ({
key: 'validation:field-too-long',
values: { max },
}),
},
})
```

#### Custom translations in validation schema

Commonly used translations with the same translation key

```tsx
yup.string().min(6 customValidators.passwordMin)
```

#### Inline translations in validation schema

Custom translations with keys defined right next to the form

```tsx
const validationSchema: yup.SchemaOf<FormData> = yup
.object()
.defined()
.shape({
password: yup.string().min(6, ({ min }) => ({
key: 'validation:password-min',
values: { min },
})),
})
```
24 changes: 0 additions & 24 deletions src/common/form/models.ts

This file was deleted.

37 changes: 0 additions & 37 deletions src/common/form/schemas.ts

This file was deleted.

13 changes: 13 additions & 0 deletions src/common/form/useForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { FormikConfig, useFormik } from 'formik'

export { translateError, customValidators } from 'common/form/validation'

export default function useForm<T>({
validateOnChange = false,
validateOnBlur = false,
...formikProps
}: FormikConfig<T>) {
const formik = useFormik({ validateOnChange, validateOnBlur, ...formikProps })

return { formik }
}
61 changes: 0 additions & 61 deletions src/common/form/useFormikHook.ts

This file was deleted.

53 changes: 53 additions & 0 deletions src/common/form/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { setLocale } from 'yup'
import { useTranslation } from 'react-i18next'

export const translateError = (
field: (string | undefined) | { key: string; values?: any },
): string | undefined => {
const { t } = useTranslation()
console.log(field)
if (!field) {
return undefined
}
if (typeof field === 'string') {
return t(field)
}
return t(field.key, field.values)
}

// Default translations:
// return 'validation:<key>'

// Default translations with interpolation:
// return { key: 'validation:<key>', values: { min, max } }

// Custom translations in validation schema:
// yup.string().min(6 customValidators.passwordMin)

// Inline translations in validation schema:
// yup.string().min(6, ({ min }) => ({ key: 'validation:password-min', values: { min } }))

export const customValidators = {
passwordMin: ({ min }: { min: number }) => ({
key: 'validation:password-min',
values: { min },
}),
}

setLocale({
mixed: {
default: 'validation:invalid',
required: 'validation:required',
},
string: {
min: ({ min }: { min: number }) => ({
key: 'validation:field-too-short',
values: { min },
}),
max: ({ max }: { max: number }) => ({
key: 'validation:field-too-long',
values: { max },
}),
email: 'validation:email',
},
})
Loading

0 comments on commit 450fbc3

Please sign in to comment.