Skip to content

Commit

Permalink
feat(Forms): deprecate Ajv errorMessages keys (pattern => `Field.…
Browse files Browse the repository at this point in the history
…errorPattern` etc.) in favor of Eufemia translation keys

This also means `validationRule` used in `FormError` is deprecated in favor of Eufemia translation keys.
  • Loading branch information
tujoworker committed Oct 18, 2024
1 parent 47bd8d3 commit ac4e982
Show file tree
Hide file tree
Showing 38 changed files with 653 additions and 337 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,22 @@ The `InputPassword` component has been moved to `Field.Password`, and is now a p

- Replace `omit_rounding` with `rounding="omit"`.

## Forms error handling

**FormError**

- Remove the `validationRule` parameter in favor of a translation key, like so: `new FormError('Field.errorRequired')`.

**errorMessages** object

- Replace `required` with `Field.errorRequired`.
- Replace `pattern` with `Field.errorPattern`.
- Replace `minLength` with `StringField.errorMinLength`.
- Replace `maxLength` with `StringField.errorMaxLength`.
- Replace `minimum` with `NumberField.errorMinimum`.
- Replace `maximum` with `NumberField.errorMaximum`.
- Replace `exclusiveMinimum` with `NumberField.errorExclusiveMinimum`.
- Replace `exclusiveMaximum` with `NumberField.errorExclusiveMaximum`.
- Replace `multipleOf` with `NumberField.errorMultipleOf`.

_February, 6. 2024_
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,24 @@
showTabs: true
---

**Table of Contents**

- [Description](#description)
- [Error object](#error-object)
- [Reuse existing error messages in a validator function](#reuse-existing-error-messages-in-a-validator-function)
- [FormError object](#formerror-object)
- [Overwrite existing keys](#overwrite-existing-keys)
- [Custom keys in a field](#custom-keys-in-a-field)
- [Custom keys in Form.Handler](#custom-keys-in-formhandler)
- [Localization of error messages](#localization-of-error-messages)
- [Use translations to localize error messages](#use-translations-to-localize-error-messages)
- [Error message in a field `schema`](#error-message-in-a-field-schema)
- [Error message in a global `schema`](#error-message-in-a-global-schema)
- [Levels of `errorMessages`](#levels-of-errormessages)

## Description

Error messages in Eufemia Forms are used to provide feedback to users when there are issues with their input.
Error messages in Eufemia Forms serve to inform users about issues with their input, ensuring they can correct mistakes and provide the necessary information accurately.

## Error object

Expand All @@ -21,6 +36,7 @@ const validator = (value) => {
// Your validation logic
return new Error('Show this message')
}

render(<Field.PhoneNumber validator={validator} />)
```

Expand All @@ -35,6 +51,7 @@ const validator = (value, { errorMessages }) => {
// Your validation logic
return new Error(errorMessages.required)
}

render(<Field.String validator={validator} />)
```

Expand All @@ -44,56 +61,78 @@ You can use the JavaScript `Error` object to display a custom error message:

```tsx
import { Field } from '@dnb/eufemia/extensions/forms'

render(<Field.PhoneNumber error={new Error('Custom message')} />)
```

When it comes to re-using existing translations, you can also use the `FormError` object to display error messages.

The `validationRule` is used to identify the error message to display.
You can provide either an existing translation key, such as:

- `required` - Displayed when the field is required and the user has not provided a value.
- `pattern` - Displayed when the user has provided a value that does not match the pattern.
- `Field.errorRequired` - Displayed when the field is required and the user has not provided a value.
- `Field.errorPattern` - Displayed when the user has provided a value that does not match the pattern.

```tsx
import { FormError, Field } from '@dnb/eufemia/extensions/forms'

// - Error property
render(<Field.PhoneNumber error={new FormError('Field.errorRequired')} />)

// - Validator function
render(
<Field.PhoneNumber
error={
new FormError('Invalid value', {
validationRule: 'pattern',
})
}
onBlurValidator={() => {
return new FormError('Field.errorRequired')
}}
/>,
)
```

Here is how you can provide validation rules, or even overwrite existing ones:
#### Overwrite existing keys

Per field, you can overwrite existing keys:

```tsx
render(
<Form.Handler
<Field.PhoneNumber
errorMessages={{
pattern: 'Display me, instead of the default message',
'Field.errorRequired': 'Display me, instead of the default message',
}}
>
...
</Form.Handler>,
/>,
)
```

For one field only:
#### Custom keys in a field

You can also provide your own keys:

```tsx
<Field.PhoneNumber
error={new FormError('MyCustom.message')}
errorMessages={{
'MyCustom.message': 'Your custom error message',
}}
/>
```

#### Custom keys in Form.Handler

Here is how you can provide your own keys or overwrite existing ones in a global `errorMessages` object inside the [Form.Handler](/uilib/extensions/forms/Form/Handler/):

```tsx
render(
<Field.PhoneNumber
<Form.Handler
errorMessages={{
pattern: 'Display me, instead of the default message',
'MyCustom.message': 'Your custom error message',
'Field.errorRequired': 'Display me, instead of the default message',
}}
/>,
>
...
</Form.Handler>,
)
```

## Localization of error messages
#### Localization of error messages

You can also provide localized error messages:

Expand All @@ -102,10 +141,11 @@ render(
<Form.Handler
errorMessages={{
'en-GB': {
pattern: 'Display me, instead of the default message',
'Field.errorRequired':
'Display me, instead of the default message',
},
'nb-NO': {
pattern: 'Vis meg istedenfor for standardmeldingen',
'Field.errorRequired': 'Vis meg istedenfor for standardmeldingen',
},
}}
>
Expand All @@ -114,15 +154,24 @@ render(
)
```

In addition, you can customize the translations globally:
#### Use translations to localize error messages

You can customize error messages via translations for the entire form:

```tsx
import { Form } from '@dnb/eufemia/extensions/forms'

render(
<Form.Handler
translations={{
'nb-NO': {
Field: { errorPattern: 'Custom pattern error' },
// - Overwrite existing keys
Field: { errorRequired: 'Display this error message instead' },
'Field.errorRequired': 'Display this error message instead',

// - Custom keys
MyCustom: { key: 'Your custom error message' },
'MyCustom.message': 'Your custom error message',
},
}}
>
Expand All @@ -131,7 +180,9 @@ render(
)
```

Or define an error message in a `schema` for one field:
#### Error message in a field `schema`

You can define an error message in a `schema` for one field:

```tsx
import { Provider } from '@dnb/eufemia/shared'
Expand All @@ -149,29 +200,9 @@ render(
)
```

Or in a field `schema` for one field with a JSON Pointer path:

```tsx
const schema = {
type: 'object',
properties: {
myKey: {
type: 'string',
pattern: '^([a-z]+)$',
errorMessage:
'You can provide a custom message in the schema itself',
},
},
} as const

render(
<Form.Handler schema={schema}>
<Field.String path="/myKey" value="123" validateInitially />
</Form.Handler>,
)
```
#### Error message in a global `schema`

Or in a Form.Handler `schema` for one field with a JSON Pointer path:
You can also define an error message in a `schema` for the entire form:

```tsx
const schema = {
Expand Down Expand Up @@ -228,7 +259,7 @@ You can provide custom error message different levels with the `errorMessages` p

The levels are prioritized in the order above, so the field level error message will overwrite all other levels.

Here is an example of how to do expose a custom error message for the `pattern` validation rule on all levels:
Here is an example of how to do expose a custom error message for the `Field.errorRequired` validation rule on all levels:

```tsx
import { Form, Field } from '@dnb/eufemia/extensions/forms'
Expand All @@ -237,18 +268,18 @@ render(
<Form.Handler
errorMessages={{
// Level 1
pattern: 'Or on the provider',
'Field.errorRequired': 'Or on the provider',
'/myKey': {
// Level 2
pattern: 'Or on the provider for just one field',
'Field.errorRequired': 'Or on the provider for just one field',
},
}}
>
<Field.String
path="/myKey"
errorMessages={{
// Level 3
pattern: 'Or on a single Field itself',
'Field.errorRequired': 'Or on a single Field itself',
}}
...
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const tr = sharedContext?.translation.Forms

const errorMessages = useErrorMessage(props.path, props.errorMessages, {
// Your default error messages
required: tr.Field.errorRequired,
pattern: tr.Field.errorPattern,
'Field.errorRequired': tr.Field.errorRequired,
'Field.errorPattern': tr.Field.errorPattern,
})

const preparedProps = {
Expand Down Expand Up @@ -123,8 +123,8 @@ render(<MyField onBlurValidator={myValidator} />)

#### Error

- `error` object like `FormError` that includes the string to display or an object with the key `validationRule`. More info down below.
- `errorMessages` object with your custom messages, where each key represents a `validationRule`. More info down below.
- `error` like `new Error()` or `new FormError()` that includes a message to display. More info down below.
- `errorMessages` object with your custom messages or translation keys, such as `'Field.errorRequired'`. More info down below.

### Return Parameters

Expand All @@ -147,9 +147,7 @@ It returns all of the given component properties, in addition to these:
```ts
const validateRequired = (value, { emptyValue, required, isChanged }) => {
if (required && value === emptyValue) {
return new FormError('This value is required', {
validationRule: 'required',
})
return new FormError('Field.errorRequired')
}
}

Expand Down Expand Up @@ -203,9 +201,7 @@ To re-use existing `errorMessages`, you can use the `FormError` constructor as w
import { FormError } from '@dnb/eufemia/extensions/forms'

// Will show the message from the errorMessages
new FormError('Internal error message', {
validationRule: 'required',
})
new FormError('Field.errorRequired')
```

In order to invoke an error without a change and blur event, you can use `validateInitially`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import AsyncChangeExample from './Form/Handler/parts/async-change-example.mdx'
- [onChange and autosave](#onchange-and-autosave)
- [Async field validation](#async-field-validation)
- [Validation and error handling](#validation-and-error-handling)
- [Error messages](#error-messages)
- [Summary for errors](#summary-for-errors)
- [required](#required)
- [pattern](#pattern)
Expand All @@ -48,7 +49,7 @@ import AsyncChangeExample from './Form/Handler/parts/async-change-example.mdx'
- [Localization and translation](#localization-and-translation)
- [Customize translations](#customize-translations)
- [How to customize translations in a form](#how-to-customize-translations-in-a-form)
- [Use the shared Provider to customize translations](#use-the-shared-provider-to-customize-translations)
- [Consume the translations](#consume-the-translations)
- [Layout](#layout)
- [Best practices](#best-practices)
- [Create your own component](#create-your-own-component)
Expand Down Expand Up @@ -401,6 +402,10 @@ Fields which have the `disabled` property or the `readOnly` property, will skip

For monitoring and setting your form errors, you can use the [useValidation](/uilib/extensions/forms/Form/useValidation) hook.

#### Error messages

Eufemia Forms comes with built-in error messages. However, you can customize and override these messages by using the `errorMessages` property both on [fields](/uilib/extensions/forms/all-fields/) and on the [Form.Handler](/uilib/extensions/forms/Form/Handler/).

#### Summary for errors

To improve user experience communication regarding errors and their locations, WCAG/UU suggests summarizing error messages when errors have occurred.
Expand All @@ -425,6 +430,8 @@ If you need a custom unique ID, you can just assign `globalStatusId` to the `For
</Form.Handler>
```

Read more about [error messages](/uilib/extensions/forms/Form/error-messages/).

#### required

The `required` property is a boolean that indicates whether the field is required or not:
Expand Down Expand Up @@ -517,7 +524,7 @@ render(

By default, the validator function will only run when the "/withValidator" field is changed. When the error message is shown, it will update the message with the new value of the "/myReference" field.

You can also change this behavior by using the following properties:
You can also change this behavior for testing purposes by using the following properties:

- `validateInitially` will run the validation initially.
- `continuousValidation` will run the validation on every change, including when the connected field changes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Ajv, makeAjvInstance } from '../utils/ajv'
import { Ajv, FormError, makeAjvInstance } from '../utils'
import {
AllJSONSchemaVersions,
CustomErrorMessagesWithPaths,
Expand All @@ -9,7 +9,6 @@ import {
EventReturnWithStateObject,
Identifier,
FieldProps,
FormError,
ValueProps,
OnChange,
OnSubmitParams,
Expand Down
Loading

0 comments on commit ac4e982

Please sign in to comment.