Skip to content

Commit 51f54ea

Browse files
Custom validation
1 parent 1ded597 commit 51f54ea

File tree

4 files changed

+122
-44
lines changed

4 files changed

+122
-44
lines changed

packages/forms/src/UIForm/UIForm.component.js

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, { PropTypes } from 'react';
22
import { reduxForm } from 'redux-form';
3-
import { merge, validate } from 'talend-json-schema-form-core';
3+
import { merge } from 'talend-json-schema-form-core';
44

55
import Widget from './Widget';
6-
import { validateAll } from './utils/validation';
6+
import { validate, validateAll } from './utils/validation';
77
import { mutateValue } from './utils/properties';
88

99
const TRIGGER_AFTER = 'after';
@@ -52,50 +52,62 @@ class UIForm extends React.Component {
5252
*/
5353
consolidate(event, schema, value) {
5454
this.setState(
55-
prevState => ({
56-
properties: mutateValue(prevState.properties, schema.key, value),
57-
validations: {
55+
(prevState) => {
56+
const properties = mutateValue(prevState.properties, schema.key, value);
57+
const validations = {
5858
...prevState.validations,
59-
[schema.key]: validate(schema, value),
60-
},
61-
}),
62-
() => {
63-
const { onChange, onTrigger } = this.props;
64-
65-
if (onChange) {
66-
onChange({
67-
jsonSchema: this.props.data.jsonSchema, // original jsonSchema
68-
uiSchema: this.props.data.uiSchema, // original uiSchema
69-
properties: this.state.properties, // current properties values
70-
});
71-
}
72-
73-
const { key, triggers } = schema;
74-
if (onTrigger && triggers && triggers.indexOf(TRIGGER_AFTER) !== -1) {
75-
onTrigger(
76-
this.state.properties, // current properties values
77-
key[key.length - 1], // field name
78-
value // field value
79-
);
80-
}
81-
}
59+
[schema.key]: validate(schema, value, properties, this.props.validation),
60+
};
61+
return { properties, validations };
62+
},
63+
() => this.handleChangesCallbacks(schema, value)
8264
);
8365
}
8466

67+
/**
68+
* Triggers the onTrigger and onChange if needed
69+
* - onChange : at each field change
70+
* - onTrigger : when schema.trigger : ['after']
71+
* @param schema The field schema
72+
* @param value The new value
73+
*/
74+
handleChangesCallbacks(schema, value) {
75+
const { onChange, onTrigger } = this.props;
76+
77+
if (onChange) {
78+
onChange({
79+
jsonSchema: this.props.data.jsonSchema, // original jsonSchema
80+
uiSchema: this.props.data.uiSchema, // original uiSchema
81+
properties: this.state.properties, // current properties values
82+
});
83+
}
84+
85+
const { key, triggers } = schema;
86+
if (onTrigger && triggers && triggers.indexOf(TRIGGER_AFTER) !== -1) {
87+
onTrigger(
88+
this.state.properties, // current properties values
89+
key[key.length - 1], // field name
90+
value // field value
91+
);
92+
}
93+
}
94+
8595
/**
8696
* Triggers a validation and update state.
8797
* @returns {boolean} true if the form is valid, false otherwise
8898
*/
8999
isValid() {
90-
const validations = validateAll(this.state.mergedSchema, this.state.properties);
91-
const keys = Object.keys(validations);
92-
for (const key of keys) {
93-
if (!validations[key].valid) {
94-
this.setState({ validations });
95-
return false;
96-
}
100+
const validations = validateAll(
101+
this.state.mergedSchema,
102+
this.state.properties,
103+
this.props.validation
104+
);
105+
106+
const isValid = Object.keys(validations).every(key => validations[key].valid);
107+
if (!isValid) {
108+
this.setState({ validations });
97109
}
98-
return true;
110+
return isValid;
99111
}
100112

101113
/**
@@ -152,9 +164,17 @@ if (process.env.NODE_ENV !== 'production') {
152164
onSubmit: PropTypes.func.isRequired,
153165
/**
154166
* Tigger > after callback.
167+
* Prototype: function onTrigger(properties, fieldName, value)
155168
* This is executed on changes on fields with uiSchema > triggers : ['after']
156169
*/
157170
onTrigger: PropTypes.func,
171+
/**
172+
* Custom validation function.
173+
* Prototype: function validation(properties, fieldName, value)
174+
* Return format : { valid: true|false, error: { message: 'my validation message' } }
175+
* This is triggered on fields that has their uiSchema > customValidation : true
176+
*/
177+
validation: PropTypes.func,
158178
};
159179
}
160180

packages/forms/src/UIForm/utils/validation.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,39 @@
1-
import { validate } from 'talend-json-schema-form-core';
1+
import { validate as staticValidate } from 'talend-json-schema-form-core';
22

33
import { getValue } from './properties';
44

55
/**
66
* Validate values.
7-
* @param mergedSchema The merged schema.
7+
* @param schema The merged schema.
8+
* @param value The value.
89
* @param properties The values.
10+
* @param customValidationFn A custom validation function
11+
* that is applied on schema.customValidation = true
12+
* @returns {object} The validation result.
13+
*/
14+
export function validate(schema, value, properties, customValidationFn) {
15+
const staticResult = staticValidate(schema, value);
16+
if (staticResult.valid && schema.customValidation && customValidationFn) {
17+
return customValidationFn(properties, schema, value);
18+
}
19+
return staticResult;
20+
}
21+
22+
/**
23+
* Validate values.
24+
* @param mergedSchema The merged schema array.
25+
* @param properties The values.
26+
* @param customValidationFn A custom validation function
27+
* that is applied on schema.customValidation = true
928
* @returns {object} The validation result by field.
1029
*/
11-
export function validateAll(mergedSchema, properties) {
30+
export function validateAll(mergedSchema, properties, customValidationFn) {
1231
const validations = {};
1332
mergedSchema.forEach((schema) => {
1433
const { key, items } = schema;
1534
if (key) {
16-
validations[key] = validate(
17-
schema,
18-
getValue(properties, key)
19-
);
35+
const value = getValue(properties, key);
36+
validations[key] = validate(schema, value, properties, customValidationFn);
2037
}
2138
if (items) {
2239
const subValidations = validateAll(items, properties);
@@ -27,7 +44,7 @@ export function validateAll(mergedSchema, properties) {
2744
}
2845

2946
/**
30-
* Check if a schema value is invalid.
47+
* Check if a schema value is valid.
3148
* It is invalid if :
3249
* - the schema is an invalid field (validations[key] = { valid: false })
3350
* - the schema has items (ex: fieldset, tabs, ...), and at least one of them is invalid

packages/forms/stories/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ sampleFilenames
6565
onTrigger={action('Trigger')}
6666
onBlur={action('Blur')}
6767
onSubmit={action('Submit')}
68+
validation={(properties, schema, value) => {
69+
action('customValidation')(properties, schema, value);
70+
return {
71+
valid: value.length < 5,
72+
error: {
73+
message: 'Custom validation : The value should be less than 5 chars',
74+
},
75+
};
76+
}}
6877
/>
6978
</section>
7079
));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"jsonSchema": {
3+
"type": "object",
4+
"title": "Comment",
5+
"properties": {
6+
"lastname": {
7+
"type": "string"
8+
},
9+
"firstname": {
10+
"type": "string"
11+
}
12+
},
13+
"required": [
14+
"lastname",
15+
"firstname"
16+
]
17+
},
18+
"uiSchema": [
19+
{
20+
"key": "lastname",
21+
"title": "Last Name",
22+
"description": "This field has custom validation (less than 5 chars)",
23+
"customValidation": true
24+
},
25+
{
26+
"key": "firstname",
27+
"title": "First Name",
28+
"description": "This field has no custom validation"
29+
}
30+
],
31+
"properties": {}
32+
}

0 commit comments

Comments
 (0)