Skip to content

Commit 536a2f5

Browse files
authored
Merge pull request #1071 from rvsia/standaloneValidation
Add ability to validate fields in the form schema separately
2 parents c21b71f + 0c6e78e commit 536a2f5

File tree

24 files changed

+783
-51
lines changed

24 files changed

+783
-51
lines changed

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

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Field } from 'react-final-form';
55
import RendererContext from '../renderer-context';
66
import Condition from '../condition';
77
import getConditionTriggers from '../get-condition-triggers';
8+
import prepareComponentProps from '../prepare-component-props';
89

910
const FormFieldHideWrapper = ({ hideField, children }) => (hideField ? <div hidden>{children}</div> : children);
1011

@@ -87,57 +88,7 @@ FormConditionWrapper.propTypes = {
8788
const SingleField = ({ component, condition, hideField, ...rest }) => {
8889
const { actionMapper, componentMapper } = useContext(RendererContext);
8990

90-
let componentProps = {
91-
component,
92-
...rest
93-
};
94-
95-
const componentBinding = componentMapper[component];
96-
let Component;
97-
if (typeof componentBinding === 'object' && Object.prototype.hasOwnProperty.call(componentBinding, 'component')) {
98-
const { component, ...mapperProps } = componentBinding;
99-
Component = component;
100-
componentProps = {
101-
...mapperProps,
102-
...componentProps,
103-
// merge mapper and field actions
104-
...(mapperProps.actions && rest.actions ? { actions: { ...mapperProps.actions, ...rest.actions } } : {}),
105-
// merge mapper and field resolveProps
106-
...(mapperProps.resolveProps && rest.resolveProps
107-
? {
108-
resolveProps: (...args) => ({
109-
...mapperProps.resolveProps(...args),
110-
...rest.resolveProps(...args)
111-
})
112-
}
113-
: {})
114-
};
115-
} else {
116-
Component = componentBinding;
117-
}
118-
119-
/**
120-
* Map actions to props
121-
*/
122-
let overrideProps = {};
123-
let mergedResolveProps; // new object has to be created because of references
124-
if (componentProps.actions) {
125-
Object.keys(componentProps.actions).forEach((prop) => {
126-
const [action, ...args] = componentProps.actions[prop];
127-
overrideProps[prop] = actionMapper[action](...args);
128-
});
129-
130-
// Merge componentProps resolve props and actions resolve props
131-
if (componentProps.resolveProps && overrideProps.resolveProps) {
132-
mergedResolveProps = (...args) => ({
133-
...componentProps.resolveProps(...args),
134-
...overrideProps.resolveProps(...args)
135-
});
136-
}
137-
138-
// do not pass actions object to components
139-
delete componentProps.actions;
140-
}
91+
const { componentProps, Component, overrideProps, mergedResolveProps } = prepareComponentProps({ component, rest, componentMapper, actionMapper });
14192

14293
return (
14394
<FormConditionWrapper condition={condition} field={componentProps}>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Schema, AnyObject, ComponentMapper } from "../common-types";
2+
import { ActionMapper } from "../form-renderer";
3+
4+
export interface GetValidatesOptions {
5+
values: AnyObject;
6+
componentMapper: ComponentMapper;
7+
actionMapper: ActionMapper;
8+
}
9+
10+
declare function getValidates(schema: Schema, options: GetValidatesOptions, validations?: AnyObject): AnyObject;
11+
12+
export default getValidates;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import get from 'lodash/get';
2+
3+
import prepareComponentProps from '../prepare-component-props';
4+
import { dataTypeValidator } from '../validators/validator-functions';
5+
6+
const getValidates = (schema, { componentMapper, actionMapper, values }, validations = {}) => {
7+
if (Array.isArray(schema)) {
8+
schema.map((field) => getValidates(field, { componentMapper, actionMapper, values }, validations));
9+
} else {
10+
if (schema.component) {
11+
let validate;
12+
13+
const { componentProps, overrideProps, mergedResolveProps } = prepareComponentProps({
14+
component: schema.component,
15+
rest: schema,
16+
componentMapper,
17+
actionMapper
18+
});
19+
20+
let resolveProps = mergedResolveProps || overrideProps.resolveProps || componentProps.resolveProps;
21+
22+
// fake form state with only values
23+
if (resolveProps) {
24+
const { validate: resolvePropsValidate } = resolveProps(
25+
schema,
26+
{ input: { value: get(values, schema.name) }, meta: {} },
27+
{ getState: () => ({ values }) }
28+
);
29+
30+
validate = resolvePropsValidate;
31+
}
32+
33+
validate = validate || overrideProps.validate || componentProps.validate;
34+
35+
if (schema.dataType) {
36+
validate = [...(validate || []), dataTypeValidator(schema.dataType)()];
37+
}
38+
39+
if (validate) {
40+
if (validations[schema.name]) {
41+
validations[schema.name].push(validate);
42+
} else {
43+
validations[schema.name] = [validate];
44+
}
45+
}
46+
}
47+
48+
if (schema.fields) {
49+
getValidates(schema.fields, { componentMapper, actionMapper, values }, validations);
50+
}
51+
}
52+
53+
return validations;
54+
};
55+
56+
export default getValidates;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './get-validates';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './get-validates';
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Schema, AnyObject } from "../common-types";
2+
3+
declare function getVisibleFields(schema: Schema, values: AnyObject ): Schema;
4+
5+
export default getVisibleFields;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import parseCondition from '../parse-condition';
2+
3+
const getVisibleFields = (schema, values) => {
4+
if (Array.isArray(schema)) {
5+
return schema.map((field) => getVisibleFields(field, values)).filter(Boolean);
6+
}
7+
8+
if (schema.condition) {
9+
const result = parseCondition(schema.condition, values, schema);
10+
11+
if (result.visible) {
12+
return {
13+
...schema,
14+
...(schema.fields && { fields: getVisibleFields(schema.fields, values).filter(Boolean) })
15+
};
16+
} else {
17+
return null;
18+
}
19+
}
20+
21+
return {
22+
...schema,
23+
...(schema.fields && { fields: getVisibleFields(schema.fields, values).filter(Boolean) })
24+
};
25+
};
26+
27+
export default getVisibleFields;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './get-visible-fields';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './get-visible-fields';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './prepare-component-props';

0 commit comments

Comments
 (0)