Import
import { Field } from '@dnb/eufemia/extensions/forms'render(<Field.String />)
Description
Field.String is the base component for receiving user input where the target data is of type string.
There is a corresponding Value.String component.
import { Field } from '@dnb/eufemia/extensions/forms'render(<Field.String path="/myValue" />)
Relevant links
Browser autofill
The string component does support HTML autocomplete attributes.
- You may either set the property directly on each field:
<Form.Handler><Field.String autoComplete="first-name" path="/your-pointer" /><Field.String autoComplete="last-name" path="/your-pointer" /></Form.Handler>
- Or use the "less-code" approach, by just giving the data pointer
patha meaningful name:
<Form.Handler autoComplete={true}><Field.String path="/firstName" /><Field.String path="/lastName" /></Form.Handler>
The path property will be used to set the name attribute.
Accessibility
Avoid using the maxlength attribute when possible, as it is not accessible. Instead, use TextCounter together with Field.String.
A demo of how to use the TextCounter with Field.String can be found here.
This way, the user receives visual feedback on the number of characters entered and the maximum allowed, without being limited in their workflow.
You can still set the desired maximum number of characters by using the maxLength property in Eufemia Forms.
Demos
Label and value
<Field.String label="Label text" defaultValue="foo" onChange={(value) => console.log('onChange', value)} />
Label and description
<Form.Card> <Field.String label="Label text" labelDescription="Description text on the next line" placeholder="Enter a text..." /> <Field.String label="Label text" labelDescription="Description text on the same line" labelDescriptionInline placeholder="Enter a text..." /> </Form.Card>
With a horizontal layout
This example uses Field.Provider to set the layout to horizontal and layoutOptions to { width: 'medium' } for all nested fields.
The width of the horizontal label can be set to small, medium, large or a rem value.
<Form.Card> <Field.Provider layout="horizontal" layoutOptions={{ width: 'medium', // can be a rem value }} placeholder="Enter a text..." required > <Field.String label="Label text" warning="Short warning." /> <Field.String label="Label with a long text that will wrap" placeholder="Enter a text..." size="medium" info="Aliqua eu aute id qui esse aliqua dolor in aute magna commodo anim enim et." /> <Field.String label="Label with a long text that will wrap" placeholder="Enter a text..." size="large" width="stretch" /> </Field.Provider> </Form.Card>
Placeholder
<Field.String label="Label text" placeholder="Enter a text..." onChange={(value) => console.log('onChange', value)} />
With a status
This example demonstrates how the status message width adjusts according to the field width.
- Error message A
- Error message B
- Warning message A
- Warning message B
- Info message A
- Info message B
<Form.Card> <Field.String label="Label text" defaultValue="foo" warning="Short warning." required /> <Field.String label="Label text" placeholder="Enter a text..." info="Aliqua eu aute id qui esse aliqua dolor in aute magna commodo anim enim et." required /> <Field.String label="Label text" defaultValue="foo" width="small" warning="Aliqua eu aute id qui esse aliqua dolor in aute magna commodo anim enim et. Velit incididunt exercitation est magna ex irure dolore nisi eiusmod ea exercitation." /> <Field.String label="Label text" error={[new Error('Error message A'), new Error('Error message B')]} warning={['Warning message A', 'Warning message B']} info={['Info message A', 'Info message B']} /> </Form.Card>
With help
<Field.String label="Label text" defaultValue="foo" help={{ title: 'Help is available', content: 'Take the time to help other people without expecting a reward or gratitude is definitely important in living an optimistic life.', }} onChange={(value) => console.log('onChange', value)} />
Capitalize each word
<Field.String label="Label text" defaultValue="foo bar" capitalize onChange={(value) => console.log('onChange', value)} />
Icons
<Form.Card> <Field.String label="Icon left" defaultValue="foo" leftIcon="check" onChange={(value) => console.log('onChange', value)} /> <Field.String label="Icon right" defaultValue="foo" rightIcon="loupe" onChange={(value) => console.log('onChange', value)} /> </Form.Card>
Clear
<Field.String defaultValue="foo" onChange={(value) => console.log('onChange', value)} clear />
Disabled
<Field.String defaultValue="foo" label="Label text" onChange={(value) => console.log('onChange', value)} disabled />
Validation - Required
<Field.String defaultValue="foo" label="Label text" onChange={(value) => console.log('onChange', value)} required />
Validation - Minimum length
<Field.String defaultValue="foo" label="Label text (minimum 8 characters)" onChange={(value) => console.log('onChange', value)} minLength={8} />
Validation - Maximum length and custom error message
<Field.String defaultValue="foo" label="Label text (maximum 8 characters)" onChange={(value) => console.log('onChange', value)} maxLength={8} errorMessages={{ maxLength: "You can't write THAT long.. Max 8 chars!", }} />
Validation - Maximum length with TextCounter
8 av 8 tegn gjenstår.
const MyFieldStringWithTextCounter = () => { const [text, setText] = React.useState('') return ( <Flex.Vertical gap="x-small"> <Field.String label="Label text (maximum 8 characters)" maxLength={8} onChange={setText} /> <TextCounter variant="down" text={text} max={8} /> </Flex.Vertical> ) } render(<MyFieldStringWithTextCounter />)
Validation - Pattern
<Field.String defaultValue="foo" label="Label text" onChange={(value) => console.log('onChange', value)} pattern="^foo123" />
Validation - Multiple Errors
<Field.String label="Multiple errors" defaultValue="foo" pattern="bar" minLength={4} validateInitially />
Synchronous external validator (called on every change)
<Field.String defaultValue="foo" label="Label text (minimum 4 characters)" onChangeValidator={(value) => value.length < 4 ? Error('At least 4 characters') : undefined } onChange={(value) => console.log('onChange', value)} />
Asynchronous external validator (called on every change)
<Field.String defaultValue="foo" label="Label text (minimum 4 characters)" onChangeValidator={(value) => new Promise((resolve) => setTimeout( () => resolve( value.length < 5 ? Error('At least 5 characters') : undefined, ), 1500, ), ) } onChange={(value) => console.log('onChange', value)} />
Synchronous external validator (called on blur)
<Field.String defaultValue="foo" label="Label text (minimum 4 characters)" onBlurValidator={(value) => value.length < 4 ? Error('At least 4 characters') : undefined } onChange={(value) => console.log('onChange', value)} />
Asynchronous external validator (called on blur)
<Field.String defaultValue="foo" label="Label text (minimum 4 characters)" onBlurValidator={(value) => new Promise((resolve) => setTimeout( () => resolve( value.length < 5 ? Error('At least 5 characters') : undefined, ), 1500, ), ) } onChange={(value) => console.log('onChange', value)} />
Multiline, empty
<Field.String onChange={(value) => console.log('onChange', value)} multiline />
Multiline, placeholder
<Field.String placeholder="Enter text here" onChange={(value) => console.log('onChange', value)} multiline />
Multiline, label & value
<Field.String defaultValue="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis in tempus odio, nec interdum orci. Integer vehicula ipsum et risus finibus, vitae commodo ex luctus. Nam viverra sollicitudin dictum. Vivamus maximus dignissim lorem, vitae viverra erat dapibus a." label="Label text" onChange={(value) => console.log('onChange', value)} multiline />
Multiline, with help
<Field.String label="Label text" help={{ title: 'Help is available', content: 'There is more happiness in giving than in receiving.', }} multiline onChange={(value) => console.log('onChange', value)} />
TransformIn and TransformOut
// From the Field (internal value) to the data context or event parameter const transformOut = (value) => { return { value, foo: 'bar', } } // To the Field (from e.g. defaultValue) const transformIn = (data) => { if (typeof data === 'string') { return data } return data?.value } const MyForm = () => { return ( <Form.Handler onSubmit={console.log}> <Form.Card> <Field.String label="String field" path="/myValue" transformIn={transformIn} transformOut={transformOut} defaultValue="Default value" /> <Value.String label="String value" path="/myValue" transformIn={transformIn} placeholder="(placeholder)" showEmpty /> <Form.SubHeading>Data Context</Form.SubHeading> <Tools.Log /> </Form.Card> <Form.SubmitButton /> </Form.Handler> ) } render(<MyForm />)
Prevent typing of invalid characters
You can use the onInput property together with the htmlAttributes property to prevent typing of invalid characters.
const forbiddenRegex = /\d/ const onInput = (event: React.FormEvent<HTMLInputElement>) => { const inputEl = event.currentTarget const oldVal = inputEl.dataset.oldVal || '' const addedLength = inputEl.value.length - oldVal.length const caretStart = inputEl.selectionStart const selectionStart = parseFloat(inputEl.dataset.selectionStart) const selectionEnd = parseFloat(inputEl.dataset.selectionEnd) let inserted = '' if (selectionStart !== selectionEnd) { inserted = inputEl.value.substring(selectionStart, selectionEnd) } else { inserted = inputEl.value.substring( caretStart - addedLength, caretStart, ) } if (forbiddenRegex.test(inserted)) { inputEl.value = oldVal const { selectionStart, selectionEnd } = inputEl.dataset if (selectionStart !== selectionEnd) { inputEl.setSelectionRange( parseFloat(selectionStart), parseFloat(selectionEnd), ) } else { inputEl.setSelectionRange( caretStart - addedLength, caretStart - addedLength, ) } } inputEl.dataset.oldVal = inputEl.value } const onFocus = (event: React.FormEvent<HTMLInputElement>) => { const inputEl = event.currentTarget if (typeof inputEl.dataset.oldVal === 'undefined') { inputEl.dataset.oldVal = inputEl.value } } const onSelect = (event: React.FormEvent<HTMLInputElement>) => { const inputEl = event.currentTarget inputEl.dataset.selectionStart = String(inputEl.selectionStart) inputEl.dataset.selectionEnd = String(inputEl.selectionEnd) } render( <Form.Handler onSubmit={console.log} onChange={console.log}> <Form.Card> <Field.String path="/myValue" label="You can't type numbers here" value="Existing value: 123" htmlAttributes={{ onFocus, onInput, onSelect, }} autoComplete="off" required /> </Form.Card> <Form.SubmitButton /> </Form.Handler>, )