Form

Multiline Box

A multiline box allows users to provide multiple inputs for the same parameter. Each input appears on a different line.
1
import {FocusEvent} from 'react';
2
import {
3
FormProvider,
4
MultilineBox,
5
IMultilineSingleBoxProps,
6
IMultilineParentProps,
7
TextInput,
8
} from '@coveord/plasma-react';
9
10
interface MyData {
11
name: string;
12
}
13
14
export default () => (
15
<FormProvider>
16
<MultilineBox<MyData>
17
id="multiline-box-id"
18
data={[{name: 'Poire'}, {name: 'Apple'}]}
19
renderBody={(data: Array<IMultilineSingleBoxProps<MyData>>, defaultProps: IMultilineParentProps) =>
20
Object.values(data).map(({id, isLast, props}: IMultilineSingleBoxProps<MyData>) => (
21
<TextInput
22
id={id}
23
key={id}
24
required={!isLast}
25
showValidationOnBlur
26
type="text"
27
label="Label"
28
defaultValue={props.name}
29
onBlur={(evt: FocusEvent<HTMLInputElement>) => {
30
if (evt.target.value !== '' && isLast) {
31
defaultProps.addNewBox();
32
}
33
}}
34
/>
35
))
36
}
37
defaultProps={{name: ''}}
38
/>
39
</FormProvider>
40
);

Props

NameTypeDefaultDescription
datarequiredunknown[]
The array of data to render
idrequiredstring
The unique identifier of the MultilineBox
defaultPropsunknown{}
An object representing the default values of a new box
disabledboolean
Whether this component is disabled
renderBody(data: IMultilineSingleBoxProps<unknown>[], parentProps: IMultilineParentProps) => ReactNode() => <div />
Function that gets the updated data and render a component
  • datathe data with new elements if any
  • parentPropssee IMultilineParentProps
renderWrapper(children: ReactNode, boxProps: IMultilineSingleBoxProps<unknown>, parentProps: IMultilineParentProps) => ReactNode
Function to wrap the content of this component in another element. Should only be used by HOC
  • childrenthe content of the MultilineBox
  • boxPropsthe props of the MultilineBox data
  • parentPropssee IMultilineParentProps

Examples

Custom container
1
import {ReactNode, FocusEvent} from 'react';
2
import {compose} from 'redux';
3
import {
4
FormProvider,
5
MultilineBox,
6
multilineBoxContainer,
7
IMultilineSingleBoxProps,
8
IMultilineParentProps,
9
TextInput,
10
} from '@coveord/plasma-react';
11
12
interface MyData {
13
name: string;
14
}
15
16
const containerNode = (child: ReactNode, data: Array<IMultilineSingleBoxProps<MyData>>, index: number) => (
17
<div key={`${data[index].id}Container`} className="p2 m1">
18
{child}
19
</div>
20
);
21
22
const MultilineBoxWithContainer = compose(multilineBoxContainer({containerNode}))(MultilineBox);
23
24
export default () => (
25
<FormProvider>
26
<div className="highlight-padding highlight-margin">
27
<MultilineBoxWithContainer
28
id="multiline-box-container-id"
29
data={[{name: 'Poire'}, {name: 'Apple'}]}
30
renderBody={(data: Array<IMultilineSingleBoxProps<MyData>>, defaultProps: IMultilineParentProps) =>
31
Object.values(data).map(({id, isLast, props}: IMultilineSingleBoxProps<MyData>) => (
32
<TextInput
33
id={id}
34
key={id}
35
required={!isLast}
36
showValidationOnBlur
37
type="text"
38
label="Label"
39
defaultValue={props.name}
40
onBlur={(evt: FocusEvent<HTMLInputElement>) => {
41
if (evt.target.value !== '' && isLast) {
42
defaultProps.addNewBox();
43
}
44
}}
45
/>
46
))
47
}
48
defaultProps={{name: ''}}
49
/>
50
</div>
51
</FormProvider>
52
);
Remove button
1
import {ReactNode, FocusEvent} from 'react';
2
import {compose} from 'redux';
3
import {
4
FormProvider,
5
MultilineBox,
6
IMultilineSingleBoxProps,
7
IMultilineParentProps,
8
TextInput,
9
multilineBoxWithRemoveButton,
10
IButtonProps,
11
defaultMultilineBoxRemoveButtonClasses,
12
} from '@coveord/plasma-react';
13
14
interface MyData {
15
name: string;
16
}
17
18
const MultilineBoxWithRemove = compose(
19
multilineBoxWithRemoveButton({
20
containerNode: (child: ReactNode, getRemoveButton: (props?: Partial<IButtonProps>) => ReactNode) => (
21
<div className="inline-flex center-align">
22
{child}
23
{getRemoveButton({
24
classes: [defaultMultilineBoxRemoveButtonClasses, 'flex-auto mb2'],
25
})}
26
</div>
27
),
28
})
29
)(MultilineBox);
30
31
export default () => (
32
<FormProvider>
33
<MultilineBoxWithRemove
34
id="multiline-box-remove-id"
35
data={[{name: 'Poire'}, {name: 'Apple'}]}
36
renderBody={(data: Array<IMultilineSingleBoxProps<MyData>>, defaultProps: IMultilineParentProps) =>
37
Object.values(data).map(({id, isLast, props}: IMultilineSingleBoxProps<MyData>) => (
38
<TextInput
39
id={id}
40
key={id}
41
required={!isLast}
42
showValidationOnBlur
43
type="text"
44
label="Label"
45
defaultValue={props.name}
46
onBlur={(evt: FocusEvent<HTMLInputElement>) => {
47
if (evt.target.value !== '' && isLast) {
48
defaultProps.addNewBox();
49
}
50
}}
51
/>
52
))
53
}
54
defaultProps={{name: ''}}
55
/>
56
</FormProvider>
57
);
Drag and drop
1
import {FocusEvent} from 'react';
2
import {compose} from 'redux';
3
import {
4
FormProvider,
5
MultilineBox,
6
IMultilineSingleBoxProps,
7
IMultilineParentProps,
8
TextInput,
9
multilineBoxWithDnD,
10
} from '@coveord/plasma-react';
11
12
interface MyData {
13
name: string;
14
}
15
16
const MultilineBoxWithDragAndDrop = compose(multilineBoxWithDnD())(MultilineBox);
17
18
export default () => (
19
<FormProvider>
20
<MultilineBoxWithDragAndDrop
21
id="multiline-box-dnd-id"
22
data={[{name: 'Poire'}, {name: 'Apple'}]}
23
renderBody={(data: Array<IMultilineSingleBoxProps<MyData>>, defaultProps: IMultilineParentProps) =>
24
Object.values(data).map(({id, isLast, props}: IMultilineSingleBoxProps<MyData>) => (
25
<TextInput
26
id={id}
27
key={id}
28
required={!isLast}
29
showValidationOnBlur
30
type="text"
31
label="Label"
32
defaultValue={props.name}
33
onBlur={(evt: FocusEvent<HTMLInputElement>) => {
34
if (evt.target.value !== '' && isLast) {
35
defaultProps.addNewBox();
36
}
37
}}
38
/>
39
))
40
}
41
defaultProps={{name: ''}}
42
/>
43
</FormProvider>
44
);
Everything combined
1
import {
2
defaultMultilineBoxRemoveButtonClasses,
3
FormProvider,
4
IButtonProps,
5
IMultilineParentProps,
6
IMultilineSingleBoxProps,
7
MultilineBox,
8
multilineBoxContainer,
9
multilineBoxWithDnD,
10
multilineBoxWithRemoveButton,
11
TextInput,
12
} from '@coveord/plasma-react';
13
import {FocusEvent, ReactNode} from 'react';
14
import {compose} from 'redux';
15
16
interface MyData {
17
name: string;
18
}
19
20
const containerNodeExample = (child: ReactNode, data: Array<IMultilineSingleBoxProps<MyData>>, index: number) => (
21
<div key={`${data[index].id}Container`} className="p1">
22
{child}
23
</div>
24
);
25
26
const ComplexMultilineBox = compose(
27
multilineBoxWithDnD(),
28
multilineBoxWithRemoveButton({
29
containerNode: (child: ReactNode, getRemoveButton: (props?: Partial<IButtonProps>) => ReactNode) => (
30
<div className="inline-flex center-align">
31
{child}
32
{getRemoveButton({
33
classes: [defaultMultilineBoxRemoveButtonClasses, 'flex-auto mb2'],
34
})}
35
</div>
36
),
37
}),
38
multilineBoxContainer({
39
containerNode: containerNodeExample,
40
})
41
)(MultilineBox);
42
43
export default () => (
44
<FormProvider>
45
<ComplexMultilineBox
46
id="multiline-box-complex-id"
47
data={[{name: 'Poire'}, {name: 'Apple'}]}
48
renderBody={(data: Array<IMultilineSingleBoxProps<MyData>>, defaultProps: IMultilineParentProps) =>
49
Object.values(data).map(({id, isLast, props}: IMultilineSingleBoxProps<MyData>) => (
50
<TextInput
51
id={id}
52
key={id}
53
required={!isLast}
54
showValidationOnBlur
55
type="text"
56
label="Label"
57
defaultValue={props.name}
58
onBlur={(evt: FocusEvent<HTMLInputElement>) => {
59
if (evt.target.value !== '' && isLast) {
60
defaultProps.addNewBox();
61
}
62
}}
63
/>
64
))
65
}
66
defaultProps={{name: ''}}
67
/>
68
</FormProvider>
69
);

Best Practices

A multiline box allows users to add multiple inputs or multiple series of inputs, each on a different line.

Inputs can be combined into a series to form a more complex input.
Text elements can also be included when an input or series requires more explanation.

A multiline box works best when the sequence of inputs is identical on all lines. Don't use the multiline box to group inputs that have no connection to one another.

Only offer the drag and drop feature when the order of the inputs or series is important. In such case, the inputs/series must always be presented in the order set by the user.

Labeling

Keep titles short, preferably under three words.

If the list of inputs is very simple, provide a descriptive title without action verbs.
For example, write "Email list" rather than "Provide a list of emails”.

If the series of inputs is complex, consider using a title with an action verb to clarify the outcome of the choice to make.
For example, write “Show content that matches..." instead of "Filter rules”.

You may choose to use a dialog approach with the labels. For example, you could have: "Among all items matching... [condition1] Show only the items with... [condition2]". In such case, consider the other inputs in the form so that your approach is coherent with all inputs.

Help Text and Instructions

Avoid adding help text or instructions for each individual input that appears in a series. Instead, provide brief instructions above the multiline box to explain the inputs in the series.

Feedback and Validation

Ensure that all inputs on the first line are complete and valid before allowing the addition of a new line.

If the multiline box is optional, don't show an empty first line. Users should add it manually if they need it. If users must fill at least one line, the first line should be visible and users should not be allowed to delete it.

Let users add new lines manually rather than automating the process. This pevents submitting forms that contain empty lines.

When each line has a series of inputs of variable width, ensure line width allows vertical alignment of the delete buttons.

Related Components

When the user must select one or more options from a single list, consider using a multi select instead. Don't use a multiline box to group inputs that have no connection to one another. Use a custom form layout instead. For example, if you want a user to provide their name and email address, just provide two text inputs. However, if you want them to provide the name and email address of several people, use a multiline box and show the name and email inputs on one line for each person.

Edit guidelines