Skip to content

Commit a664ba9

Browse files
authored
Merge pull request #801 from rvsia/carbonFieldArray
feat(carbon): add field array component
2 parents 8ab13af + 9a712c6 commit a664ba9

File tree

6 files changed

+578
-54
lines changed

6 files changed

+578
-54
lines changed

packages/carbon-component-mapper/demo/demo-schemas/widget-schema.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export const arraySchemaDDF = {
1414
name: 'name',
1515
label: 'Name',
1616
placeholder: 'Borek',
17-
isRequired: true,
1817
validate: [
1918
{
2019
type: 'required'
@@ -63,7 +62,6 @@ export const arraySchemaDDF = {
6362
fields: [
6463
{
6564
component: 'text-field',
66-
isRequired: true,
6765
validate: [
6866
{
6967
type: 'required'

packages/carbon-component-mapper/demo/index.js

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
33
import FormRenderer from '@data-driven-forms/react-form-renderer';
4-
// import { arraySchemaDDF } from './demo-schemas/widget-schema';
4+
import { arraySchemaDDF } from './demo-schemas/widget-schema';
55
import { componentMapper, FormTemplate } from '../src';
66
import { wizardSchema } from './demo-schemas/wizard-schema';
77
import sandboxSchema from './demo-schemas/sandbox';
@@ -10,7 +10,7 @@ import demoSchema from '@data-driven-forms/common/src/demoschema';
1010
import { Button } from 'carbon-components-react';
1111

1212
const fieldArrayState = {
13-
schema: demoSchema,
13+
schema: arraySchemaDDF,
1414
additionalOptions: {
1515
initialValues: {
1616
number: [1, 2, 3, 4],
@@ -45,38 +45,7 @@ class App extends React.Component {
4545
componentMapper={componentMapper}
4646
FormTemplate={(props) => <FormTemplate {...props} showFormControls={this.state.additionalOptions.showFormControls} />}
4747
onCancel={console.log}
48-
//schema={this.state.schema}
49-
schema={{
50-
fields: [
51-
{
52-
component: 'dual-list-select',
53-
name: 'dual-list',
54-
label: 'select animal',
55-
options: [
56-
{
57-
value: 'cats',
58-
label: 'cats'
59-
},
60-
{
61-
value: 'cats_1',
62-
label: 'cats_1'
63-
},
64-
{
65-
value: 'cats_2',
66-
label: 'cats_2'
67-
},
68-
{
69-
value: 'zebras',
70-
label: 'zebras'
71-
},
72-
{
73-
value: 'pigeons',
74-
label: 'pigeons'
75-
}
76-
]
77-
}
78-
]
79-
}}
48+
schema={this.state.schema}
8049
{...this.state.additionalOptions}
8150
/>
8251
</div>
Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
1-
import { FieldArrayField } from "@data-driven-forms/react-form-renderer";
1+
import { FieldArrayField, UseFieldApiComponentConfig } from "@data-driven-forms/react-form-renderer";
2+
import { ReactNode } from "react";
3+
import { ButtonProps, FormGroupProps as CarbonFormGroupProps } from "carbon-components-react";
24

3-
export interface FieldArrayProps {
5+
import { FormGroupProps } from './form-group';
6+
7+
export interface FieldArrayButtonLabels {
8+
add: ReactNode;
9+
remove: ReactNode;
10+
}
11+
12+
export interface InternalFieldArrayProps {
413
fields: FieldArrayField[];
5-
itemDefault?: any;
14+
defaultItem?: any;
15+
minItems?: number;
16+
maxItems?: number;
17+
noItemsMessage?: ReactNode;
18+
buttonLabels?: FieldArrayButtonLabels;
19+
AddContainerProps?: React.HTMLProps<HTMLDivElement>;
20+
AddButtonProps?: ButtonProps;
21+
RemoveButtonProps?: ButtonProps;
22+
ArrayItemProps?: React.HTMLProps<HTMLDivElement>;
23+
FormGroupProps?: CarbonFormGroupProps;
24+
WrapperProps?: React.HTMLProps<HTMLDivElement>;
625
}
726

27+
export type FieldArrayProps = InternalFieldArrayProps & FormGroupProps & UseFieldApiComponentConfig;
28+
829
declare const FieldArray: React.ComponentType<FieldArrayProps>;
930

1031
export default FieldArray;
Lines changed: 128 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3+
import clsx from 'clsx';
4+
35
import { useFieldApi, useFormApi, FieldArray as FieldArrayFF } from '@data-driven-forms/react-form-renderer';
46

5-
const ArrayItem = ({ remove, fields, name }) => {
7+
import { Button, FormGroup } from 'carbon-components-react';
8+
import { AddAlt32, Subtract32 } from '@carbon/icons-react';
9+
10+
import './field-array.scss';
11+
12+
import prepareProps from '../common/prepare-props';
13+
14+
const ArrayItem = ({ remove, fields, name, removeText, buttonDisabled, RemoveButtonProps, ArrayItemProps }) => {
615
const formOptions = useFormApi();
716

817
const editedFields = fields.map((field) => ({
@@ -11,34 +20,138 @@ const ArrayItem = ({ remove, fields, name }) => {
1120
}));
1221

1322
return (
14-
<div>
23+
<div {...ArrayItemProps}>
1524
{formOptions.renderForm(editedFields, formOptions)}
16-
<button onClick={remove}>Remove</button>
25+
<Button
26+
disabled={buttonDisabled}
27+
renderIcon={Subtract32}
28+
id={`remove-${name}`}
29+
kind="danger"
30+
onClick={remove}
31+
{...RemoveButtonProps}
32+
className={clsx('ddorg__carbon-field-array-remove', RemoveButtonProps.className)}
33+
>
34+
{removeText}
35+
</Button>
1736
</div>
1837
);
1938
};
2039

2140
ArrayItem.propTypes = {
2241
remove: PropTypes.func,
2342
fields: PropTypes.array,
24-
name: PropTypes.string
43+
name: PropTypes.string,
44+
removeText: PropTypes.node,
45+
buttonDisabled: PropTypes.bool,
46+
RemoveButtonProps: PropTypes.object,
47+
ArrayItemProps: PropTypes.object
48+
};
49+
50+
ArrayItem.defaultProps = {
51+
RemoveButtonProps: {},
52+
ArrayItemProps: {}
2553
};
2654

2755
const FieldArray = (props) => {
28-
const { itemDefault, fields, input, arrayValidator } = useFieldApi(props);
56+
const {
57+
AddContainerProps,
58+
AddButtonProps,
59+
FormGroupProps,
60+
WrapperProps,
61+
ArrayItemProps,
62+
RemoveButtonProps,
63+
defaultItem,
64+
maxItems,
65+
minItems,
66+
fields,
67+
input,
68+
arrayValidator,
69+
labelText,
70+
buttonLabels,
71+
noItemsMessage,
72+
meta
73+
} = useFieldApi(prepareProps(props));
74+
75+
const buttonLabelsFinal = {
76+
add: 'Add',
77+
remove: 'Remove',
78+
...buttonLabels
79+
};
80+
81+
const invalid = meta.touched && !Array.isArray(meta.error) && meta.error;
2982

3083
return (
31-
<FieldArrayFF name={input.name} validate={arrayValidator}>
32-
{(fieldArrayProps) => (
33-
<div>
34-
{fieldArrayProps.fields.map((name, index) => (
35-
<ArrayItem key={index} remove={() => fieldArrayProps.fields.remove(index)} name={name} fields={fields} />
36-
))}
37-
<button onClick={() => fieldArrayProps.fields.push(itemDefault)}>Add</button>
38-
</div>
39-
)}
40-
</FieldArrayFF>
84+
<FormGroup
85+
legendText={labelText || ''}
86+
invalid={Boolean(invalid)}
87+
message={Boolean(invalid)}
88+
messageText={invalid || ''}
89+
{...FormGroupProps}
90+
className={clsx('ddorg__carbon-field-array-form-group', FormGroupProps.className)}
91+
>
92+
<FieldArrayFF name={input.name} validate={arrayValidator}>
93+
{(fieldArrayProps) => (
94+
<div {...WrapperProps}>
95+
{fieldArrayProps.fields.length === 0 && noItemsMessage}
96+
{fieldArrayProps.fields.map((name, index) => (
97+
<ArrayItem
98+
removeText={buttonLabelsFinal.remove}
99+
key={index}
100+
remove={() => fieldArrayProps.fields.remove(index)}
101+
name={name}
102+
fields={fields}
103+
buttonDisabled={minItems >= fieldArrayProps.fields.length}
104+
ArrayItemProps={ArrayItemProps}
105+
RemoveButtonProps={RemoveButtonProps}
106+
/>
107+
))}
108+
<div {...AddContainerProps} className={clsx('ddorg__carbon-field-array-add-container', AddContainerProps.className)}>
109+
<Button
110+
disabled={fieldArrayProps.fields.length >= maxItems}
111+
renderIcon={AddAlt32}
112+
id={`add-${input.name}`}
113+
onClick={() => fieldArrayProps.fields.push(defaultItem)}
114+
{...AddButtonProps}
115+
className={clsx('ddorg__carbon-field-array-add', AddButtonProps.className)}
116+
>
117+
{buttonLabelsFinal.add}
118+
</Button>
119+
</div>
120+
</div>
121+
)}
122+
</FieldArrayFF>
123+
</FormGroup>
41124
);
42125
};
43126

127+
FieldArray.propTypes = {
128+
noItemsMessage: PropTypes.node,
129+
maxItems: PropTypes.number,
130+
minItems: PropTypes.number,
131+
buttonLabels: PropTypes.shape({
132+
add: PropTypes.node,
133+
remove: PropTypes.node
134+
}),
135+
AddContainerProps: PropTypes.object,
136+
AddButtonProps: PropTypes.object,
137+
FormGroupProps: PropTypes.object,
138+
WrapperProps: PropTypes.object,
139+
ArrayItemProps: PropTypes.object,
140+
RemoveButtonProps: PropTypes.object,
141+
defaultItem: PropTypes.any,
142+
fields: PropTypes.array
143+
};
144+
145+
FieldArray.defaultProps = {
146+
noItemsMessage: 'No items',
147+
maxItems: Infinity,
148+
minItems: 0,
149+
AddContainerProps: {},
150+
AddButtonProps: {},
151+
FormGroupProps: {},
152+
WrapperProps: {},
153+
ArrayItemProps: {},
154+
RemoveButtonProps: {}
155+
};
156+
44157
export default FieldArray;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.ddorg__carbon-field-array-add {
2+
margin-top: 16px;
3+
}
4+
5+
.ddorg__carbon-field-array-add-container {
6+
width: 100%;
7+
display: flex;
8+
flex-direction: row-reverse;
9+
}
10+
11+
.ddorg__carbon-field-array-remove {
12+
margin-top: 8px;
13+
margin-bottom: 8px;
14+
}
15+
16+
.ddorg__carbon-field-array-form-group {
17+
.bx--form__requirements {
18+
color: #da1e28;
19+
}
20+
}

0 commit comments

Comments
 (0)