diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx
index 536e97fc1f4..2727e272a38 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/Examples.tsx
@@ -5,6 +5,26 @@ import { Button, Card, Flex, P, Section } from '@dnb/eufemia/src'
import { debounceAsync } from '@dnb/eufemia/src/shared/helpers/debounce'
import { createRequest } from '../SubmitIndicator/Examples'
+export const RequiredAndOptionalFields = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
export const AsyncSubmit = () => {
return (
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx
index 29ad9fd2c71..79aa7645246 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/Handler/demos.mdx
@@ -6,6 +6,14 @@ import * as Examples from './Examples'
## Demos
+### Required and Optional Fields
+
+To make all fields required, set the `required` prop on the `Form.Handler` component.
+
+For fields that should remain optional, use `required={false}` prop on the specific field. When doing so, it will append "(optional)" to the optional field's label(`labelSuffix`).
+
+
+
### In combination with a SubmitButton
This example uses an async `onSubmit` event handler. It will disable all fields and show an indicator on the [SubmitButton](/uilib/extensions/forms/Form/SubmitButton/) while the form is pending.
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
index a547591fccd..cb782599759 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
@@ -329,7 +329,24 @@ The `required` property is a boolean that indicates whether the field is require
```
-**NB:** You can also use the `required` property on the [Form.Handler](/uilib/extensions/forms/Form/Handler/) component, [Wizard.Step](/uilib/extensions/forms/Wizard/Step/) component or nested in the [Form.FieldProps](/uilib/extensions/forms/Form/FieldProps/) component.
+**Note:** You can use the `required` property on the [Form.Handler](/uilib/extensions/forms/Form/Handler/) or [Wizard.Step](/uilib/extensions/forms/Wizard/Step/) components ([example](/uilib/extensions/forms/Form/Handler/demos/#required-and-optional-fields)). Additionally, the [Form.Section](/uilib/extensions/forms/Form/Section/) component as well as the [Form.FieldProps](/uilib/extensions/forms/Form/FieldProps/) provider has a `required` property, which will make all nested fields within that section required.
+
+```tsx
+
+
+
+
+```
+
+When you need to opt-out of the required field validation, you can use the `required={false}` property. This will also add a "(optional)" suffix to the field label(`labelSuffix`).
+
+```tsx
+
+
+
+
+
+```
#### pattern
diff --git a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx
index 8d73c819a68..a7ebc84d102 100644
--- a/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/DataContext/Provider/__tests__/Provider.test.tsx
@@ -4226,6 +4226,97 @@ describe('DataContext.Provider', () => {
})
})
+ describe('required={false}', () => {
+ it('should add optional label', () => {
+ const { rerender } = render(
+
+
+
+
+
+ )
+
+ const [first, second, third] = Array.from(
+ document.querySelectorAll('label')
+ )
+
+ expect(first).toHaveTextContent('Foo')
+ expect(second).toHaveTextContent(
+ `Bar ${nb.Field.optionalLabelSuffix}`
+ )
+ expect(third).toHaveTextContent('Baz')
+
+ rerender(
+
+
+
+
+
+ )
+
+ expect(first).toHaveTextContent(
+ `Foo ${nb.Field.optionalLabelSuffix}`
+ )
+ expect(second).toHaveTextContent('Bar')
+ expect(third).toHaveTextContent(
+ `Baz ${nb.Field.optionalLabelSuffix}`
+ )
+ })
+
+ it('should prioritize labelSuffix over optionalLabel', () => {
+ render(
+
+
+
+ )
+
+ const labelElement = document.querySelector('label')
+ expect(labelElement.textContent).toBe('e-post (suffix)')
+ })
+
+ it('should hide labelSuffix with empty string', () => {
+ render(
+
+
+
+ )
+
+ const labelElement = document.querySelector('label')
+ expect(labelElement.textContent).toBe('e-post')
+ })
+
+ it('should support translations', () => {
+ render(
+
+
+
+
+
+ )
+
+ const [first, second, third] = Array.from(
+ document.querySelectorAll('label')
+ )
+
+ expect(first).toHaveTextContent('Foo')
+ expect(second).toHaveTextContent('Bar (recommended)')
+ expect(third).toHaveTextContent('Baz')
+ })
+ })
+
describe('required', () => {
it('should make all fields required', () => {
const { rerender } = render(
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/Email/__tests__/Email.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/Email/__tests__/Email.test.tsx
index 560d627d4eb..8891ff62059 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/Email/__tests__/Email.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/Email/__tests__/Email.test.tsx
@@ -115,6 +115,26 @@ describe('Field.Email', () => {
expect(input).toHaveAttribute('type', 'email')
})
+ it('should have default label', () => {
+ render()
+
+ const label = document.querySelector('label')
+ expect(label).toHaveTextContent(nb.Email.label)
+ })
+
+ it('should add (optional) text to the label if required={false}', () => {
+ render(
+
+
+
+ )
+
+ const label = document.querySelector('label')
+ expect(label).toHaveTextContent(
+ `${nb.Email.label} ${nb.Field.optionalLabelSuffix}`
+ )
+ })
+
it('should allow a custom pattern', async () => {
render()
diff --git a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx
index d2df977660d..7155eb4f0a1 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Field/PhoneNumber/__tests__/PhoneNumber.test.tsx
@@ -57,6 +57,40 @@ describe('Field.PhoneNumber', () => {
expect(labelElement()).not.toHaveAttribute('disabled')
})
+ it('should have default label', () => {
+ render()
+
+ const label = document.querySelector('.dnb-forms-field-phone-number')
+ expect(label).toHaveTextContent(nbNO.PhoneNumber.label)
+ })
+
+ it('should add (optional) text to the number label if required={false}', () => {
+ render(
+
+
+
+ )
+
+ const codeElement = document.querySelector(
+ '.dnb-forms-field-phone-number__country-code'
+ ) as HTMLInputElement
+ const numberElement = document.querySelector(
+ '.dnb-forms-field-phone-number__number'
+ ) as HTMLInputElement
+
+ expect(codeElement.querySelector('label')).not.toHaveTextContent(
+ `${nbNO.Field.optionalLabelSuffix}`
+ )
+ expect(numberElement.querySelector('label')).toHaveTextContent(
+ `${nbNO.PhoneNumber.label} ${nbNO.Field.optionalLabelSuffix}`
+ )
+
+ // Use "textContent" to check against non-breaking space
+ expect(numberElement.querySelector('label').textContent).toBe(
+ `${nbNO.PhoneNumber.label}${' '}${nbNO.Field.optionalLabelSuffix}`
+ )
+ })
+
it('should only have a mask when +47 is given', async () => {
const { rerender } = render()
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx
index 29739884b56..5b32854b5d3 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/FieldBlock.tsx
@@ -50,6 +50,7 @@ export type Props = Pick<
| keyof ComponentProps
| 'layout'
| 'label'
+ | 'labelSuffix'
| 'labelDescription'
| 'info'
| 'warning'
@@ -76,6 +77,8 @@ export type Props = Pick<
fieldState?: SubmitState
/** Typography size */
labelSize?: 'medium' | 'large'
+ /** For internal use only */
+ required?: boolean
children?: React.ReactNode
} & React.HTMLAttributes
@@ -93,8 +96,10 @@ function FieldBlock(props: Props) {
composition,
label: labelProp,
labelDescription,
+ labelSuffix,
labelSrOnly,
asFieldset,
+ required,
info,
warning,
error: errorProp,
@@ -122,15 +127,46 @@ function FieldBlock(props: Props) {
return Boolean(errorProp)
}, []) // eslint-disable-line react-hooks/exhaustive-deps
+ const { optionalLabelSuffix } = useTranslation().Field
+ const labelSuffixText = useMemo(() => {
+ if (required === false || typeof labelSuffix !== 'undefined') {
+ return labelSuffix ?? optionalLabelSuffix
+ }
+ return ''
+ }, [required, labelSuffix, optionalLabelSuffix])
+
const label = useMemo(() => {
+ let content = labelProp
+
if (iterateIndex !== undefined) {
- return convertJsxToString(labelProp).replace(
+ content = convertJsxToString(labelProp).replace(
'{itemNr}',
String(iterateIndex + 1)
)
}
- return labelProp
- }, [iterateIndex, labelProp])
+
+ if (labelSuffixText) {
+ if (convertJsxToString(content).includes(optionalLabelSuffix)) {
+ return content
+ }
+
+ if (typeof content === 'string') {
+ return content + ' ' + labelSuffixText
+ }
+
+ if (React.isValidElement(content)) {
+ return (
+ <>
+ {content}
+ {' '}
+ {labelSuffixText}
+ >
+ )
+ }
+ }
+
+ return content
+ }, [iterateIndex, labelProp, labelSuffixText])
const setInternalRecord = useCallback((props: StateBasis) => {
const { stateId, identifier, type } = props
diff --git a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx
index 6e8d32dba47..6297ff4b7ac 100644
--- a/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/FieldBlock/__tests__/FieldBlock.test.tsx
@@ -10,6 +10,11 @@ import {
simulateAnimationEnd,
} from '../../../../components/height-animation/__tests__/HeightAnimationUtils'
import { Field, Form } from '../..'
+import nbNO from '../../constants/locales/nb-NO'
+import enGB from '../../constants/locales/en-GB'
+
+const nb = nbNO['nb-NO']
+const en = enGB['en-GB']
describe('FieldBlock', () => {
it('should forward HTML attributes', () => {
@@ -137,6 +142,130 @@ describe('FieldBlock', () => {
expect(labelElement).toHaveTextContent('A Label Description')
})
+ describe('labelSuffix', () => {
+ it('should add additional text to the label with a non-breaking space', () => {
+ render(
+
+ content
+
+ )
+
+ const labelElement = document.querySelector('label')
+
+ expect(labelElement).toBeInTheDocument()
+ expect(labelElement).toHaveTextContent(
+ `A Label ${nb.Field.optionalLabelSuffix}`
+ )
+ expect(
+ labelElement.querySelector('span > span').innerHTML
+ ).toContain(`A Label (valgfritt)`)
+ })
+
+ it('should add additional text to the label with a non-breaking space when label and labelSuffix is a JSX element', () => {
+ render(
+ A Label}
+ labelSuffix={(valgfritt)}
+ >
+ content
+
+ )
+
+ const labelElement = document.querySelector('label')
+
+ expect(labelElement).toBeInTheDocument()
+ expect(labelElement).toHaveTextContent(
+ `A Label ${nb.Field.optionalLabelSuffix}`
+ )
+ expect(labelElement.querySelector('span').innerHTML).toContain(
+ `A Label (valgfritt)`
+ )
+ })
+ })
+
+ describe('required={false}', () => {
+ it('should add (optional) text to the label', () => {
+ render(
+
+ content
+
+ )
+
+ const labelElement = document.querySelector('label')
+
+ expect(labelElement).toBeInTheDocument()
+ expect(labelElement).toHaveTextContent(
+ `A Label ${nb.Field.optionalLabelSuffix}`
+ )
+ })
+
+ it('should prioritize labelSuffix over optionalLabel', () => {
+ render(
+
+ content
+
+ )
+
+ const labelElement = document.querySelector('label')
+ expect(labelElement.textContent).toBe('A Label (suffix)')
+ })
+
+ it('should check if labelSuffix already exists in label', () => {
+ render(
+
+ content
+
+ )
+
+ const labelElement = document.querySelector('label')
+ expect(labelElement.textContent).toBe(
+ `My Label ${nb.Field.optionalLabelSuffix}`
+ )
+ })
+
+ it('should support en-GB locale', () => {
+ render(
+
+
+ content
+
+
+ )
+
+ const labelElement = document.querySelector('label')
+
+ expect(labelElement).toBeInTheDocument()
+ expect(labelElement).toHaveTextContent(
+ `A Label ${en.Field.optionalLabelSuffix}`
+ )
+ })
+
+ it('should add (optional) text when label is a JSX element', () => {
+ render(
+ A Label} required={false}>
+ content
+
+ )
+
+ const labelElement = document.querySelector('label')
+
+ expect(labelElement).toBeInTheDocument()
+ expect(labelElement).toHaveTextContent(
+ `A Label ${nb.Field.optionalLabelSuffix}`
+ )
+ expect(labelElement.querySelector('span').innerHTML).toContain(
+ `A Label (valgfritt)`
+ )
+ })
+ })
+
it('click on label should set focus on input after value change', async () => {
const MockComponent = () => {
const fromInput = React.useCallback(({ value }) => value, [])
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.screenshot.test.ts b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.screenshot.test.ts
new file mode 100644
index 00000000000..58e90a891e5
--- /dev/null
+++ b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/Handler.screenshot.test.ts
@@ -0,0 +1,18 @@
+/**
+ * Screenshot Test
+ * This file will not run on "test:staged" because we don't require any related files
+ */
+
+import { makeScreenshot } from '../../../../../core/jest/jestSetupScreenshots'
+
+describe('Form.Handler', () => {
+ const url = '/uilib/extensions/forms/Form/Handler/demos'
+
+ it('have to match required and optional fields', async () => {
+ const screenshot = await makeScreenshot({
+ url,
+ selector: '[data-visual-test="required-and-optional-fields"]',
+ })
+ expect(screenshot).toMatchImageSnapshot()
+ })
+})
diff --git a/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/__image_snapshots__/formhandler-have-to-match-required-and-optional-fields.snap.png b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/__image_snapshots__/formhandler-have-to-match-required-and-optional-fields.snap.png
new file mode 100644
index 00000000000..7ee53a4c1a9
Binary files /dev/null and b/packages/dnb-eufemia/src/extensions/forms/Form/Handler/__tests__/__image_snapshots__/formhandler-have-to-match-required-and-optional-fields.snap.png differ
diff --git a/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx b/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx
index ede9615e6d2..ed1b6499381 100644
--- a/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/Value/String/__tests__/String.test.tsx
@@ -2,6 +2,9 @@ import React from 'react'
import { screen, render } from '@testing-library/react'
import { Field, Form, Value, Wizard } from '../../..'
import userEvent from '@testing-library/user-event'
+import nbNO from '../../../constants/locales/nb-NO'
+
+const nb = nbNO['nb-NO']
describe('Value.String', () => {
it('renders value', () => {
@@ -58,6 +61,32 @@ describe('Value.String', () => {
).toHaveTextContent('The label')
})
+ it('should only show optional label on the field label if required={false}', () => {
+ render(
+
+
+
+
+ )
+ expect(
+ document.querySelector('.dnb-forms-field-string')
+ ).toHaveTextContent(`The label ${nb.Field.optionalLabelSuffix}`)
+ expect(
+ document.querySelector('.dnb-forms-value-string')
+ ).toHaveTextContent('The label')
+ expect(
+ document.querySelector('.dnb-forms-value-string')
+ ).not.toHaveTextContent(nb.Field.optionalLabelSuffix)
+ })
+
it('should not use label from field with same path when label is false', () => {
render(
{
document.querySelector('.dnb-forms-value-string')
).toHaveTextContent('The label')
})
+
+ it('should not inherit labelSuffix in value label', () => {
+ render(
+
+
+
+
+ )
+
+ expect(document.querySelector('label').textContent).toBe(
+ `The label${' '}${nb.Field.optionalLabelSuffix}`
+ )
+ expect(
+ document.querySelector('.dnb-forms-value-string').textContent
+ ).toBe('The label')
+ })
})
it('renders default class', () => {
diff --git a/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts b/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts
index 18873752946..2ec23ee403e 100644
--- a/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/constants/locales/en-GB.ts
@@ -9,6 +9,7 @@ export default {
errorSummary: 'Please correct the following errors:',
errorRequired: 'This field is required.',
errorPattern: 'The value is invalid.',
+ optionalLabelSuffix: '(optional)',
},
SubmitButton: {
text: 'Send',
diff --git a/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts b/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts
index a83ff630201..8b3ff6c54b3 100644
--- a/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/constants/locales/nb-NO.ts
@@ -9,6 +9,7 @@ export default {
errorSummary: 'Feil som må rettes:',
errorRequired: 'Dette feltet må fylles ut.',
errorPattern: 'Verdien er ugyldig.',
+ optionalLabelSuffix: '(valgfritt)',
},
SubmitButton: {
text: 'Send',
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts
index 82a62c207ba..3a8e70a2065 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/DataValueDocs.ts
@@ -42,10 +42,15 @@ export const dataValueProperties: PropertiesTableProps = {
status: 'optional',
},
required: {
- doc: 'When set `true`, the field will give an error if the value cannot be empty.',
+ doc: 'When set to `true`, the field will give an error if the value fails the required validation. When set to `false`, the field will not be required, but will add a "(optional)" suffix to the label.',
type: 'boolean',
status: 'optional',
},
+ labelSuffix: {
+ doc: 'Will append an additional text to the label, like "(optional)". When using `inheritLabel`, the suffix will not be inherited. NB: The visual appearance of the `labelSuffix` may change in the future.',
+ type: 'React.Node',
+ status: 'optional',
+ },
schema: {
doc: 'Custom JSON Schema for validating the value.',
type: 'object',
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx
index 8c6f1fb445d..141aacf7939 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useFieldProps.test.tsx
@@ -2000,6 +2000,18 @@ describe('useFieldProps', () => {
expect(result.current.htmlAttributes).toEqual({})
})
+ it('should return empty htmlAttributes when optional prop is true and required prop is true', async () => {
+ const { result } = renderHook(() =>
+ useFieldProps({
+ value: undefined,
+ required: false,
+ })
+ )
+
+ expect(result.current.htmlAttributes).toEqual({})
+ expect(result.current.error).not.toBeInstanceOf(Error)
+ })
+
describe('required', () => {
it('should return aria-required=true when required prop is true', async () => {
const { result } = renderHook(() =>
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
index 372dce6f508..a4c3ec105bb 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/useFieldProps.ts
@@ -211,7 +211,7 @@ export default function useFieldProps(
const hasFocusRef = useRef()
const required = useMemo(() => {
- if (requiredProp) {
+ if (typeof requiredProp !== 'undefined') {
return requiredProp
}
@@ -242,7 +242,7 @@ export default function useFieldProps(
return true
}
}
- }, [sectionPath, dataContext.schema, identifier, requiredProp, schema])
+ }, [requiredProp, identifier, schema, dataContext.schema, sectionPath])
// Error handling
// - Should errors received through validation be shown initially. Assume that providing a direct prop to
@@ -1700,6 +1700,8 @@ export default function useFieldProps(
info: !inFieldBlock ? infoRef.current : undefined,
warning: !inFieldBlock ? warningRef.current : undefined,
error: !inFieldBlock ? error : undefined,
+ required,
+ labelSuffix: props.labelSuffix,
/** HTML Attributes */
disabled:
diff --git a/packages/dnb-eufemia/src/extensions/forms/types.ts b/packages/dnb-eufemia/src/extensions/forms/types.ts
index 763b8d436eb..a44ca318a37 100644
--- a/packages/dnb-eufemia/src/extensions/forms/types.ts
+++ b/packages/dnb-eufemia/src/extensions/forms/types.ts
@@ -245,6 +245,10 @@ export type FieldBlockProps = {
* Main label text for the field
*/
label?: React.ReactNode
+ /**
+ * Will append an additional text to the label, like "(optional)" or "(recommended)"
+ */
+ labelSuffix?: React.ReactNode
/**
* A more discreet text displayed beside the label
*/