Skip to content

Commit bb51570

Browse files
authored
Merge pull request #987 from rvsia/fieldProps
feat(renderer): introduce FieldProps
2 parents a59955a + aa90df6 commit bb51570

File tree

8 files changed

+100
-38
lines changed

8 files changed

+100
-38
lines changed

packages/common/src/dual-list-select/dual-list-select.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ const DualListSelectCommon = (props) => {
3333

3434
const { DualListSelect, ...rest } = useFieldApi({
3535
...props,
36-
isEqual: (current, initial) => isEqual([...(current || [])].sort(), [...(initial || [])].sort())
36+
FieldProps: {
37+
isEqual: (current, initial) => isEqual([...(current || [])].sort(), [...(initial || [])].sort())
38+
}
3739
});
3840

3941
const leftValues = rest.options

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import fieldArraySchema from './demo-schemas/field-array-schema';
1212

1313
import Button from '@material-ui/core/Button';
1414
import wizardSchema from './demo-schemas/wizard-schema';
15-
import validatorTypes from '@data-driven-forms/react-form-renderer/dist/cjs/validator-types';
15+
import validatorTypes from '@data-driven-forms/react-form-renderer/validator-types';
1616

1717
const theme = createMuiTheme({
1818
typography: {

packages/pf4-component-mapper/src/dual-list-select/dual-list-select.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ const DualList = (props) => {
2626
...rest
2727
} = useFieldApi({
2828
...props,
29-
isEqual: (current, initial) => isEqual([...(current || [])].sort(), [...(initial || [])].sort())
29+
FieldProps: {
30+
isEqual: (current, initial) => isEqual([...(current || [])].sort(), [...(initial || [])].sort())
31+
}
3032
});
3133

3234
const [sortConfig, setSortConfig] = useState(() => ({ left: isSortable && 'asc', right: isSortable && 'asc' }));

packages/react-form-renderer/src/tests/form-renderer/render-form.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1650,7 +1650,7 @@ describe('renderForm function', () => {
16501650

16511651
expect(wrapper.find('label').text()).toEqual(label);
16521652
expect(resolveProps).toHaveBeenCalledWith(
1653-
{ label: 'standard label' },
1653+
{ label: 'standard label', component: 'custom-component' },
16541654
expect.objectContaining({ meta: expect.any(Object), input: expect.any(Object) }),
16551655
expect.any(Object)
16561656
);

packages/react-form-renderer/src/tests/form-renderer/use-field-api.test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,19 @@ describe('useFieldApi', () => {
215215
wrapper.update();
216216
expect(wrapper.find('input')).toHaveLength(1);
217217
});
218+
219+
it('omits FieldProps', () => {
220+
const parse = jest.fn().mockImplementation((value) => value);
221+
initialProps = {...initialProps, FieldProps: { parse }};
222+
223+
const wrapper = mount(<WrapperComponent {...initialProps} />);
224+
225+
expect(wrapper.find(Catcher).props().FieldProps).toEqual(undefined);
226+
expect(parse.mock.calls).toHaveLength(0);
227+
228+
wrapper.find('input').simulate('change', { target: { value: 'ABC' } });
229+
wrapper.update();
230+
231+
expect(parse.mock.calls).toHaveLength(1);
232+
});
218233
});

packages/react-form-renderer/src/use-field-api/use-field-api.js

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import composeValidators from './compose-validators';
1010
import isEqual from 'lodash/isEqual';
1111

1212
const calculateInitialValue = (props) => {
13-
if (Object.prototype.hasOwnProperty.call(props, 'initialValue') && Object.prototype.hasOwnProperty.call(props, 'dataType')) {
13+
if (Object.prototype.hasOwnProperty.call(props, 'initialValue') && props.dataType) {
1414
return convertInitialValue(props.initialValue, props.dataType);
1515
}
1616
};
@@ -84,33 +84,57 @@ const createFieldProps = (name, formOptions) => {
8484
};
8585
};
8686

87-
const useFieldApi = ({ name, initializeOnMount, component, render, validate, resolveProps, useWarnings, ...props }) => {
87+
const useFieldApi = ({
88+
name,
89+
resolveProps,
90+
...props
91+
}) => {
8892
const { validatorMapper, formOptions } = useContext(RendererContext);
8993
const [warning, setWarning] = useState();
9094

91-
const { validate: resolvePropsValidate, ...resolvedProps } = resolveProps
95+
const resolvedProps = resolveProps
9296
? resolveProps(props, createFieldProps(name, formOptions), formOptions) || {}
9397
: {};
9498

95-
const finalValidate = resolvePropsValidate || validate;
99+
const combinedProps = { ...props, ...resolvedProps};
100+
const {
101+
initializeOnMount,
102+
component,
103+
render,
104+
validate,
105+
useWarnings,
106+
clearOnUnmount,
107+
dataType,
108+
FieldProps,
109+
...rest
110+
} = combinedProps;
96111

97112
const [{ type, initialValue, validate: stateValidate, arrayValidator }, dispatch] = useReducer(
98113
reducer,
99-
{ props: { ...props, ...resolvedProps }, validate: finalValidate, component, validatorMapper, setWarning, useWarnings },
114+
{ props: combinedProps, validate, component, validatorMapper, setWarning, useWarnings },
100115
init
101116
);
102117

103118
const mounted = useRef(false);
104119

105120
const enhancedProps = {
106-
type,
107-
...props,
108-
...resolvedProps,
121+
dataType,
122+
type: combinedProps.type,
123+
...(Object.prototype.hasOwnProperty.call(combinedProps, 'initialValue')
124+
? { initialValue: combinedProps.initialValue }
125+
: {}
126+
),
127+
...(Object.prototype.hasOwnProperty.call(combinedProps, 'value')
128+
? { value: combinedProps.value }
129+
: {}
130+
),
131+
...FieldProps,
132+
...(type ? { type } : {}),
109133
...(initialValue ? { initialValue } : {}),
110134
...(stateValidate ? { validate: stateValidate } : {})
111135
};
112136

113-
const fieldProps = useField(name, enhancedProps);
137+
const field = useField(name, enhancedProps);
114138

115139
/** Reinitilize type */
116140
useEffect(() => {
@@ -127,8 +151,8 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, res
127151
if (mounted.current) {
128152
dispatch({
129153
type: 'setValidators',
130-
validate: calculateValidate(enhancedProps, finalValidate, component, validatorMapper, setWarning, useWarnings),
131-
arrayValidator: calculateArrayValidator(enhancedProps, finalValidate, component, validatorMapper)
154+
validate: calculateValidate(enhancedProps, validate, component, validatorMapper, setWarning, useWarnings),
155+
arrayValidator: calculateArrayValidator(enhancedProps, validate, component, validatorMapper)
132156
});
133157
}
134158
/**
@@ -137,7 +161,7 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, res
137161
* Using stringify is acceptable here since the array is usually very small.
138162
* If we notice performance hit, we can implement custom hook with a deep equal functionality.
139163
*/
140-
}, [finalValidate ? JSON.stringify(finalValidate) : false, component, enhancedProps.dataType]);
164+
}, [validate ? JSON.stringify(validate) : false, component, dataType]);
141165

142166
/** Re-convert initialValue when changed */
143167
useEffect(() => {
@@ -150,7 +174,7 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, res
150174
});
151175
}
152176
}
153-
}, [enhancedProps.initialValue, enhancedProps.dataType]);
177+
}, [enhancedProps.initialValue, dataType]);
154178

155179
useEffect(() => {
156180
/**
@@ -161,33 +185,33 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, res
161185
const value = Object.prototype.hasOwnProperty.call(enhancedProps, 'initialValue')
162186
? enhancedProps.initialValue
163187
: formOptions.getFieldState(name).initial;
164-
fieldProps.input.onChange(value);
188+
field.input.onChange(value);
165189
}
166-
}, [initializeOnMount, enhancedProps.initialValue, fieldProps.meta.initial, props.dataType]);
190+
}, [initializeOnMount, enhancedProps.initialValue, field.meta.initial, dataType]);
167191

168192
/**
169193
* Prepare deleted value of field
170194
*/
171-
const fieldClearedValue = Object.prototype.hasOwnProperty.call(props, 'clearedValue') ? props.clearedValue : formOptions.clearedValue;
195+
const fieldClearedValue = Object.prototype.hasOwnProperty.call(rest, 'clearedValue') ? rest.clearedValue : formOptions.clearedValue;
172196

173197
useEffect(
174198
() => {
175199
mounted.current = true;
176-
if (fieldProps.input.type === 'file') {
177-
formOptions.registerInputFile(fieldProps.input.name);
200+
if (field.input.type === 'file') {
201+
formOptions.registerInputFile(field.input.name);
178202
}
179203

180204
return () => {
181205
mounted.current = false;
182206
/**
183207
* Delete the value from form state when field is inmounted
184208
*/
185-
if ((formOptions.clearOnUnmount || props.clearOnUnmount) && props.clearOnUnmount !== false) {
186-
fieldProps.input.onChange(fieldClearedValue);
209+
if ((formOptions.clearOnUnmount || clearOnUnmount) && clearOnUnmount !== false) {
210+
field.input.onChange(fieldClearedValue);
187211
}
188212

189-
if (fieldProps.input.type === 'file') {
190-
formOptions.unRegisterInputFile(fieldProps.input.name);
213+
if (field.input.type === 'file') {
214+
formOptions.unRegisterInputFile(field.input.name);
191215
}
192216
};
193217
},
@@ -197,38 +221,33 @@ const useFieldApi = ({ name, initializeOnMount, component, render, validate, res
197221

198222
const {
199223
initialValue: _initialValue,
200-
clearOnUnmount,
201-
dataType,
202224
clearedValue,
203-
isEqual: _isEqual,
204-
validate: _validate,
205-
type: _type,
206225
...cleanProps
207-
} = enhancedProps;
226+
} = rest;
208227

209228
/**
210229
* construct component props necessary that would live in field provider
211230
*/
212231
return {
213232
...cleanProps,
214-
...fieldProps,
233+
...field,
215234
...(arrayValidator && { arrayValidator }),
216235
...(useWarnings && {
217236
meta: {
218-
...fieldProps.meta,
237+
...field.meta,
219238
warning
220239
}
221240
}),
222241
input: {
223-
...fieldProps.input,
242+
...field.input,
224243
value:
225-
fieldProps.input.type === 'file' && typeof fieldProps.input.value === 'object' ? fieldProps.input.value.inputValue : fieldProps.input.value,
244+
field.input.type === 'file' && typeof field.input.value === 'object' ? field.input.value.inputValue : field.input.value,
226245
onChange: (...args) => {
227246
enhancedOnChange(
228247
{
229-
...fieldProps.meta,
248+
...field.meta,
230249
dataType,
231-
onChange: fieldProps.input.onChange,
250+
onChange: field.input.onChange,
232251
clearedValue: fieldClearedValue
233252
},
234253
...args

packages/react-renderer-demo/src/pages/migration-guide-v3.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,19 @@ FormRenderer component is no longer a default export of the `react-form-renderer
6666

6767
All the mappers except Ant Design and PatternFly 4 were migrated to use JSS (CSS in JS), so you don't have to use any css-loader. (However, you still need to setup the loader in case you are using ant-component-mapper of pf4-component-mapper. If you are using PF4, you are probably using the loader right now, as you need it also for the core package.)
6868

69+
## FieldProps introduction
70+
71+
A new [FieldProps](/schema/introduction#fieldprops) props is introduced. This object serves to pass values to Final Form [useField configuration](https://final-form.org/docs/react-final-form/types/FieldProps). Do not use for natively supported props (`initialValue`, `validate`, ...).
72+
73+
```diff
74+
{
75+
name: 'component-with-complex-isEqual',
76+
component: 'dual-list-selector',
77+
- isEqual: (a, b) => isEqual(a, b),
78+
+ FieldProps: {
79+
+ isEqual: (a, b) => isEqual(a, b),
80+
+ }
81+
}
82+
```
83+
6984
</DocPage>

packages/react-renderer-demo/src/pages/schema/introduction.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Other attribues, such as title or description, can be used in [form templates](/
2929
clearOnUnmount: true,
3030
condition: { ... },
3131
dataType: 'string',
32+
FieldProps: { ... },
3233
hideField: true,
3334
initializeOnMount: true,
3435
initialValue: 'default-login',
@@ -132,6 +133,14 @@ Data type sets the type the value will be converted to. Read more [here](/schema
132133

133134
---
134135

136+
### FieldProps
137+
138+
*object*
139+
140+
You can pass additional [Final Form FieldProps](https://final-form.org/docs/react-final-form/types/FieldProps) via FieldProps object. This prop is made to avoid conflicts between Final Form props and component props.
141+
142+
---
143+
135144
### hideField
136145

137146
*bool*

0 commit comments

Comments
 (0)