The Basics

What it is

MultiField provides a responsive layout for a row of related inputs. Grouping related inputs together saves vertical space and helps users enter data. It’s similar to FormField: where FormField provides a responsive vertical layout, MultiField provides a responsive horizontal layout.  

How it works

  • The user clicks or focuses on any input in MultiField, which makes that input active. 
  • While an input is active, the user can enter and edit data in it. 
  • The user navigates between inputs. 
  • If one input fails validation, an error message for that input appears below. 
  • When finished, the user navigates away from the MultiField.

When to use

MultiField is for pieces of information that are related to each other and also meaningful on their own, such as:  

  • The fields for a patient’s name: First name, Middle initial, Last name 
  • Data points about a location: City, State, ZIP code 

When not to use

MultiField isn’t the right choice for pieces of information that mean something together but lose meaning when separated, such as: 

  • Height, broken into 2 inputs: feet and inches 
  • Weight, broken into 2 inputs: lb and oz 
  • Social Security number, broken into 3 inputs separated by dashes 

Also, don’t use MultiField:  

  • Inside FormField, which already has its own set of labels, error messages, and hint text 
  • To group unrelated inputs in order to save space 
  • For a range of dates: Start date, End date 

What to use instead

Input

Use Input for data fields that aren't closely related. 

FormField

Use FormField for a responsive vertical layout.

DateRangeInput

Use DateRangeInput to input a range of dates: Start date, End date.

How to use

Labels and hint text

MultiField offers 2 kinds of label: one for the group, and an individual label for each input. Group labels aren’t always necessary, but individual labels are strongly encouraged. 

Do:

Use a unique group label when appropriate. 

<p>Use a unique group label when appropriate.&nbsp;</p>
Don't:

Use a group label that’s redundant or confusing. 

<p>Use a group label that’s redundant or confusing.&nbsp;</p>

MultiField can display a single hint text message below the row of inputs. The individual fields don’t have their own hint text. 

Error messages 

MultiField supports separate error messages for each input. These messages appear below the row of inputs, aligned to the left. If there are multiple error messages, each message sits on a separate line, in the order of the corresponding inputs (left to right). 

If the inputs wrap, all error messages appear below the last input. This can make it hard for users to know which input caused the error.

Do:

Arrange related inputs in a single row. 

<p>Arrange related inputs in a single row.&nbsp;</p>
Don't:

Use so many inputs that they wrap on screens <640px wide. 

<p>Use so many inputs that they wrap on screens &lt;640px wide.&nbsp;</p>

Required fields 

MultiField can include both required and optional fields. For example, when requesting a patient’s first name, middle initial, and last name, the first and last name inputs can be required, while the middle initial input is optional. 

Style

Design details

The spacing between inputs in MultiField defaults to 8px, but it can be customized. Keep inputs close together so it’s clear that they’re a group.

Spacing and size

The height of this component and the vertical space around it vary according to the form layout (i.e., medium, compact, super-compact). See Form for details.

Placement and hierarchy

No additional information for this component.

Content

Use sentence case for label text (“Patient name”, not “Patient Name”).

Use sentence case for any placeholder text. If providing an example, use “e.g.,” as an introduction. For example:

  • Email address: “e.g., person@email.com”

Use sentence case for any hint text. It should apply to the whole MultiField, not just one of the inputs.

Demos

Multi Field Basic Share

Multi Field Custom Inputs Share

Coding

Developer tips

MultiField supports multiple inputs in the same row. The fields prop expects an array of objects, where each object is similar to the props supported by FormField. This is because the inputs behave as if each were in its own FormField. Each has its own labelText, value, onChange, and so on. Apart from this, the API is similar to FormField. The props table shows which props are input-specific and which apply to the entire row.

Type Validation

MultiField offers an "opt-in" approach to typing. One can utilize MultiField in either "Strict" or "Flexible" mode.

Strict Mode
  • MultiField accepts a variety of prop signatures; specifically, up to five tuple types. These positional arguments correspond to the item at the respective index of the fields array.
  • For Example, <MultiField<SelectProps, InputProps, CheckBoxProps> fields={fields} ... /> would enforce typing as follows: fields[0] would be subject to SelectProps; fields[1] would be subject to InputProps; and fields[2] would be subject to CheckBoxProps, and so on for up to five possible items. Importantly, for a fields array that contains more than 5 items, those additional fields will be rendered, but this strict typing will not be available.
Flexible Mode
  • Simply do not provide any prop signatures and strict typing will not be explicitly enforced. However, even in the absence of positional arguments, MultiField will infer the type of a field from the value (i.e., the component) supplied to its inputAs prop. So, even in Flexible Mode, type safety will still be provided where possible.

Input widths

Fitting multiple inputs in a single row can require greater control over input widths. When determining the widths, keep 2 things in mind:

  • Don’t let inputs wrap on any supported viewport. When the inputs wrap, all error messages appear below the last input. This makes it hard for users to match the errors to their corresponding inputs.
  • The width of the input determines how much space its label has before it wraps. For very small inputs, use short labels.

To set the width of an input (and its label), you can add a class with the className prop within fields and target it in CSS.

Alternately, you can set a style prop in the fields object to set the width inline (see first demo).

fields = {
[
{
labelText: 'MI',
id: 'middleInitial',
style: { width: '5ch' },
},
]
}

Hint text

MultiField shows only a single line of hint text, so all props related to hint text are at the top level, not in the fields array.

ReduxMultiField component

Redux features were removed in Forge 3, including the 3 Redux-based components: ReduxForm, ReduxFormField, and ReduxMultiField (see the FAQ for details). Forge 3 still supports the use of Redux. Teams can use the separate package forge-redux-form.

Required fields

Individual fields within MultiField can be marked as required. Use the required prop on the individual fields provided in the fields prop. Set the required prop on the MultiField itself to apply required styling to the overall component (e.g., to style the text label).

Forge offers 3 options to indicate required form fields. When using Form with this component, set Form’s requiredVariation prop; it is then passed down to the individual fields. See Form for details.

Repository

Implementation links

MultiField directory in Bitbucket

Implementation details

It is strongly recommended to familiarize yourself with the Forge source code. While this documentation is a best effort to document the intent and usage of a component, sometimes some features only become clear when looking at the source code. Also, looking at Forge's source code may help identify and fix bugs in either your application or Forge itself.

Storybook files

Forge maintains at least one storybook file per component. While the primary audience for these files is typically the Forge team, these storybook files may cover usages of the component not covered by a demo. The storybook for the latest version of forge can be found at go/forge-storybook.

Testing library

Forge strongly encourages using testing-library to write tests for your application.

"The more your tests resemble the way your software is used, the more confidence they can give you."

If you're having trouble testing a Forge component using testing-library, it would be a good idea to see how Forge tests its own components. For the most part, Forge tries to use screen.getByRole as much as it can, as that API provides the best feedback on a11y compliance. Forge discourages the use of document.querySelector and screen.getByTestId as both APIs encourage using implementation details to test your component, and discourage adding roles to your component.

With that being said, many of Forge's components were not built with accessability in mind. These components do break the recommendations listed above.

Import statements

In Nimbus applications

athenaOne serves the Forge bundle independently from your application's bundle. Importing Forge components directly from '@athena/forge' takes advantage of this feature.

import { MultiField } from '@athena/forge'

In standalone applications

Importing components using the exact path to the module takes advantage of webpack's tree shaking feature. Webpack will include only that module and its dependencies.

import MultiField from '@athena/forge/MultiField';

To use this import guidance, Typescript applications must use typescript >= 4.7.3, and should add this setting to their tsconfig.json file:

{
"compilerOptions": {
"moduleResolution": "Node16",
}
}

If this setting doesn't work for your application, use this import statement instead:

import MultiField from '@athena/forge/dist/MultiField';

Props