Skip to content

Commit 52bf4a7

Browse files
committed
Update demo and component
1 parent 0130936 commit 52bf4a7

File tree

9 files changed

+281
-68
lines changed

9 files changed

+281
-68
lines changed

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,70 @@ export const array1Schema = {
407407
},
408408
};
409409

410+
export const arraySchemaDDF = {
411+
title: 'FieldArray',
412+
fields: [
413+
{
414+
component: 'field-array',
415+
name: 'nicePeople',
416+
fieldKey: 'field_array',
417+
label: 'Nice people',
418+
description: 'This allow you to add nice people to the list dynamically',
419+
itemDefault: { name: 'enter a name', lastName: 'enter a last name' },
420+
fields: [{
421+
component: 'text-field',
422+
name: 'name',
423+
label: 'Name',
424+
placeholder: 'Borek',
425+
isRequired: true,
426+
validate: [{
427+
type: 'required-validator',
428+
}],
429+
}, {
430+
component: 'text-field',
431+
name: 'lastName',
432+
label: 'Last Name',
433+
placeholder: 'Stavitel',
434+
}],
435+
},
436+
{
437+
component: 'field-array',
438+
name: 'minItems',
439+
label: 'A list with a minimal number of items',
440+
validate: [{ type: 'min-items-validator', threshold: 3 }],
441+
fields: [{
442+
component: 'text-field',
443+
label: 'Item',
444+
}],
445+
},
446+
{
447+
component: 'field-array',
448+
name: 'number',
449+
defaultItem: 5,
450+
label: 'Default value with initialValues set',
451+
fields: [{
452+
component: 'text-field',
453+
label: 'Item',
454+
type: 'number',
455+
}],
456+
},
457+
{
458+
component: 'field-array',
459+
name: 'minMax',
460+
minItems: 4,
461+
maxItems: 6,
462+
label: 'Min 4 item, max 6 items without validators',
463+
fields: [{
464+
component: 'text-field',
465+
isRequired: true,
466+
validate: [{
467+
type: 'required-validator',
468+
}],
469+
}],
470+
},
471+
],
472+
};
473+
410474
export const uiArraySchema = {
411475
listOfStrings: {
412476
items: {

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@ import React from "react";
33
import ReactDOM from "react-dom";
44
import FormRenderer from '@data-driven-forms/react-form-renderer';
55
import miqSchema from './demo-schemas/miq-schema';
6-
import { uiArraySchema, arraySchema, array1Schema, schema, uiSchema, conditionalSchema } from './demo-schemas/widget-schema';
6+
import { uiArraySchema, arraySchema, array1Schema, schema, uiSchema, conditionalSchema, arraySchemaDDF } from './demo-schemas/widget-schema';
77
import { formFieldsMapper, layoutMapper } from '../src';
88
import { Title, Button, Toolbar, ToolbarGroup } from '@patternfly/react-core';
99
import { wizardSchema, wizardSchemaSimple, wizardSchemaSubsteps, wizardSchemaMoreSubsteps } from './demo-schemas/wizard-schema';
1010
import sandboxSchema from './demo-schemas/sandbox';
1111

1212
const Summary = props => <div>Custom summary component.</div>;
13+
14+
const fieldArrayState = { schema: arraySchemaDDF, schemaString: 'default', ui: uiArraySchema, additionalOptions: {
15+
initialValues: {
16+
number: [1,2,3,4],
17+
minMax: [null, null, null, null]
18+
}
19+
}};
20+
1321
class App extends React.Component {
1422
constructor(props) {
1523
super(props);
16-
this.state = { schema: array1Schema, schemaString: 'mozilla', ui: uiArraySchema, additionalOptions: {}};
24+
this.state = fieldArrayState
1725
}
1826

1927
render() {
@@ -25,7 +33,7 @@ class App extends React.Component {
2533
<Button onClick={() => this.setState(state => ({ schema: wizardSchema, schemaString: 'default', additionalOptions: { showFormControls: false, wizard: true } }))}>Wizard</Button>
2634
</ToolbarGroup>
2735
<ToolbarGroup>
28-
<Button onClick={() => this.setState(state => ({ schema: arraySchema, schemaString: 'mozilla', ui: uiArraySchema, additionalOptions: {}}))}>arraySchema</Button>
36+
<Button onClick={() => this.setState(state => fieldArrayState)}>arraySchema</Button>
2937
</ToolbarGroup>
3038
<ToolbarGroup>
3139
<Button onClick={() => this.setState(state => ({ schema: schema, schemaString: 'mozilla', ui: uiSchema, additionalOptions: {}}))}>schema</Button>

packages/pf4-component-mapper/src/form-fields/fieldArray/final-form-array.scss

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
color: var(--pf-global--icon--Color--light);
33
height: 70%;
44

5-
&:hover {
5+
&:hover:not(.disabled) {
66
color: var(--pf-global--icon--Color--dark);
77
cursor: pointer;
88
}
9+
10+
&.disabled {
11+
opacity: 0.5
12+
}
913
};
1014

1115
.final-form-array-add-container {
@@ -17,10 +21,14 @@
1721
min-height: 24px;
1822
min-width: 24px;
1923

20-
&:hover {
24+
&:hover:not(.disabled) {
2125
color: var(--pf-global--icon--Color--dark);
2226
cursor: pointer;
2327
}
28+
29+
&.disabled {
30+
opacity: 0.5
31+
}
2432
};
2533

2634

@@ -30,4 +38,8 @@
3038

3139
.ddf-final-form-hr {
3240
color: var(global_BorderColor_100);
33-
}
41+
}
42+
43+
.ddf-final-form-array-grid:not(:last-child) {
44+
margin-bottom: var(--pf-global--spacer--md);
45+
}

packages/pf4-component-mapper/src/form-fields/fieldArray/index.js

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,21 @@ import './final-form-array.scss';
77

88
const ArrayItem = ({
99
fields,
10-
fieldKey,
1110
fieldIndex,
1211
name,
1312
remove,
1413
formOptions,
14+
length,
15+
minItems,
1516
}) => {
1617
const widths = {
17-
label: fields[0].label ? 7 : 0,
18-
field: fields[0].label ? 4 : 11,
18+
label: fields[0].label ? 5 : 0,
19+
field: fields[0].label ? 7 : 12,
1920
};
2021

21-
const editedFields = fields.map((field) => {
22-
const itemName = field.name
23-
? field.name.substring(field.name.lastIndexOf('.') + 1)
24-
: `${fieldKey}[${fieldIndex}]`;
25-
const fieldName = name ? `${name}${itemName && itemName !== 'items' ? itemName : ''}` : `${fieldKey}[${fieldIndex}]`;
26-
return { ...field, name: fieldName, key: name, hideLabel: true };
22+
const editedFields = fields.map((field, index) => {
23+
const computedName = field.name ? `${name}.${field.name}` : name;
24+
return { ...field, name: computedName, key: `${name}-${index}`, hideLabel: true };
2725
});
2826

2927
return (
@@ -33,25 +31,36 @@ const ArrayItem = ({
3331
<hr className="ddf-final-form-hr" />
3432
</GridItem>
3533
</Grid>
36-
<Grid className="wrapper">
37-
{ widths.label > 0 && <GridItem className="ddf-final-form-group-label" sm={ widths.label }>
38-
{ <label htmlFor={ editedFields[0].name }>{ fields[0].label }</label> }
39-
</GridItem> }
40-
<GridItem sm={ widths.field } className="final-form-array-item">
41-
{ formOptions.renderForm(editedFields) }
34+
<Grid>
35+
<GridItem sm={ 11 }>
36+
{ editedFields.map((field, index) => (
37+
<Grid key={ `${field.label}-${index}` } className="ddf-final-form-array-grid">
38+
{ widths.label > 0 &&
39+
<GridItem sm={ widths.label } key={ `${field.label}-${index}` }>
40+
<label htmlFor={ field.name }>{ field.label }</label>
41+
</GridItem> }
42+
<GridItem sm={ widths.field }>
43+
{ formOptions.renderForm([ field ]) }
44+
</GridItem>
45+
</Grid>
46+
)) }
4247
</GridItem>
4348
<GridItem sm={ 1 }>
49+
{ length > minItems &&
4450
<Bullseye>
4551
<CloseIcon onClick={ () => remove(fieldIndex) } className="ddf-final-form-group-remove-icon"/>
46-
</Bullseye>
52+
</Bullseye> }
53+
{ length <= minItems &&
54+
<Bullseye>
55+
<CloseIcon className="ddf-final-form-group-remove-icon disabled"/>
56+
</Bullseye> }
4757
</GridItem>
4858
</Grid>
4959
</React.Fragment>
5060
);
5161
};
5262

5363
ArrayItem.propTypes = {
54-
fieldKey: PropTypes.string.isRequired,
5564
name: PropTypes.string,
5665
fieldIndex: PropTypes.number.isRequired,
5766
fields: PropTypes.arrayOf(PropTypes.object),
@@ -60,43 +69,54 @@ ArrayItem.propTypes = {
6069
};
6170

6271
const DynamicArray = ({
63-
fieldKey,
6472
arrayValidator,
65-
title,
73+
label,
6674
description,
67-
fields,
68-
itemDefault,
75+
fields: formFields,
76+
defaultItem,
6977
formOptions,
7078
meta,
7179
reactFinalForm,
7280
FieldArrayProvider,
81+
minItems,
82+
maxItems,
83+
noItemsMessage,
7384
...rest
7485
}) => {
7586
const { dirty, submitFailed, error } = meta;
7687
const isError = (dirty || submitFailed) && error && typeof error === 'string';
7788

7889
return (
79-
<FieldArrayProvider key={ fieldKey } name={ rest.input.name } validate={ arrayValidator }>
80-
{ (cosi) => (
90+
<FieldArrayProvider key={ rest.input.name } name={ rest.input.name } validate={ arrayValidator }>
91+
{ ({ fields: { map, value = [], push, remove }}) => (
8192
<Fragment>
82-
{ title && <GridItem sm={ 12 }><h3>{ title }</h3></GridItem> }
93+
{ label && <GridItem sm={ 12 }><h3>{ label }</h3></GridItem> }
8394
{ description && <GridItem sm={ 12 }><p>{ description }</p></GridItem> }
84-
{ cosi.fields.map((name, index) => (
95+
{ value.length <= 0 && <Bullseye>
96+
<GridItem sm={ 12 }>{ noItemsMessage }</GridItem>
97+
</Bullseye> }
98+
{ map((name, index) => (
8599
<ArrayItem
86-
key={ `${name || fieldKey}-${index}` }
87-
fields={ fields }
100+
key={ `${name}-${index}` }
101+
fields={ formFields }
88102
name={ name }
89-
fieldKey={ fieldKey }
90103
fieldIndex={ index }
91-
remove={ cosi.fields.remove }
104+
remove={ remove }
92105
formOptions={ formOptions }
106+
length={ value.length }
107+
minItems={ minItems }
93108
/>)) }
94109
<Grid>
95110
<GridItem sm={ 11 }>{ isError && <FormHelperText isHidden={ false } isError={ true }>{ error }</FormHelperText> }</GridItem>
96111
<GridItem sm={ 1 } className="final-form-array-add-container">
112+
{ value.length < maxItems &&
97113
<Bullseye>
98-
<AddCircleOIcon onClick={ () => cosi.fields.push(itemDefault) } className="ddf-final-form-group-add-icon"/>
99-
</Bullseye>
114+
<AddCircleOIcon onClick={ () => push(defaultItem) } className="ddf-final-form-group-add-icon"/>
115+
</Bullseye> }
116+
{ value.length >= maxItems &&
117+
<Bullseye>
118+
<AddCircleOIcon className="ddf-final-form-group-add-icon disabled"/>
119+
</Bullseye> }
100120
</GridItem>
101121
</Grid>
102122
</Fragment>
@@ -105,32 +125,20 @@ const DynamicArray = ({
105125
};
106126

107127
DynamicArray.propTypes = {
108-
fieldKey: PropTypes.string,
109128
arrayValidator: PropTypes.func,
110-
title: PropTypes.string,
111-
description: PropTypes.string,
129+
label: PropTypes.node,
130+
description: PropTypes.node,
112131
fields: PropTypes.arrayOf(PropTypes.object),
113-
itemDefault: PropTypes.any,
114-
};
115-
116-
const renderArrayField = props => {
117-
const { arrayValidator, ...rest } = props;
118-
return (
119-
<DynamicArray
120-
fieldKey={ rest.input.name }
121-
{ ...rest }
122-
arrayValidator={ arrayValidator }
123-
/>
124-
);
125-
};
126-
127-
renderArrayField.propTypes = {
128-
arrayValidator: PropTypes.array,
129-
input: PropTypes.any,
132+
defaultItem: PropTypes.any,
133+
minItems: PropTypes.number,
134+
maxItems: PropTypes.number,
135+
noItemsMessage: PropTypes.node,
130136
};
131137

132-
renderArrayField.defaultProps = {
133-
validate: [],
138+
DynamicArray.defaultProps = {
139+
maxItems: Infinity,
140+
minItems: 0,
141+
noItemsMessage: 'No items added',
134142
};
135143

136-
export default renderArrayField;
144+
export default DynamicArray;

packages/react-form-renderer/src/form-renderer/field-wrapper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const FieldWrapper = ({ componentType, validate, component, ...rest }) => {
1818
component,
1919
};
2020
if (shouldAssignFormOptions(componentType)) {
21-
componentProps.arrayValidator = value => {
21+
componentProps.arrayValidator = (value = []) => {
2222
if (!Array.isArray(value)) {
2323
return;
2424
}

packages/react-form-renderer/src/parsers/default-schema-validator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ const iterateOverFields = (fields, formFieldsMapper, layoutMapper, parent = {})
135135

136136
}
137137

138-
if (!field.hasOwnProperty('name') && !field.hasOwnProperty('title') && !field.hasOwnProperty('key')) {
138+
if (!field.hasOwnProperty('name') && !field.hasOwnProperty('title') && !field.hasOwnProperty('key') && parent.component !== 'field-array') {
139139
throw new DefaultSchemaError(`Each fields item must have "name" or "key" property! Name is used as a unique identifier of form fields.`);
140140
}
141141

packages/react-form-renderer/src/validators/index.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ export const length = memoize(({
2121
equal = selectNum(equal, is);
2222
min = selectNum(min, minimum);
2323
max = selectNum(max, maximum);
24-
return prepare (value => {
25-
if (!value) {
26-
return;
27-
}
28-
24+
return prepare ((value = []) => {
2925
if (equal !== null && value.length !== equal) {
3026
const msg = prepareMsg(message, 'wrongLength', { count: equal }).defaultMessage;
3127
return typeof msg === 'string' ? msg : msg(equal);

0 commit comments

Comments
 (0)