Skip to content

Commit

Permalink
Custom validation
Browse files Browse the repository at this point in the history
  • Loading branch information
jsomsanith-tlnd committed May 15, 2017
1 parent 1ded597 commit 1aac98a
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 39 deletions.
81 changes: 50 additions & 31 deletions packages/forms/src/UIForm/UIForm.component.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { merge, validate } from 'talend-json-schema-form-core';
import { merge } from 'talend-json-schema-form-core';

import Widget from './Widget';
import { validateAll } from './utils/validation';
import { validate, validateAll } from './utils/validation';
import { mutateValue } from './utils/properties';

const TRIGGER_AFTER = 'after';
Expand Down Expand Up @@ -56,46 +56,57 @@ class UIForm extends React.Component {
properties: mutateValue(prevState.properties, schema.key, value),
validations: {
...prevState.validations,
[schema.key]: validate(schema, value),
[schema.key]: validate(schema, value, prevState.properties, this.props.validation),
},
}),
() => {
const { onChange, onTrigger } = this.props;

if (onChange) {
onChange({
jsonSchema: this.props.data.jsonSchema, // original jsonSchema
uiSchema: this.props.data.uiSchema, // original uiSchema
properties: this.state.properties, // current properties values
});
}

const { key, triggers } = schema;
if (onTrigger && triggers && triggers.indexOf(TRIGGER_AFTER) !== -1) {
onTrigger(
this.state.properties, // current properties values
key[key.length - 1], // field name
value // field value
);
}
}
() => this.handleChangesCallbacks(schema, value)
);
}

/**
* Triggers the onTrigger and onChange if needed
* - onChange : at each field change
* - onTrigger : when schema.trigger : ['after']
* @param schema The field schema
* @param value The new value
*/
handleChangesCallbacks(schema, value) {
const { onChange, onTrigger } = this.props;

if (onChange) {
onChange({
jsonSchema: this.props.data.jsonSchema, // original jsonSchema
uiSchema: this.props.data.uiSchema, // original uiSchema
properties: this.state.properties, // current properties values
});
}

const { key, triggers } = schema;
if (onTrigger && triggers && triggers.indexOf(TRIGGER_AFTER) !== -1) {
onTrigger(
this.state.properties, // current properties values
key[key.length - 1], // field name
value // field value
);
}
}

/**
* Triggers a validation and update state.
* @returns {boolean} true if the form is valid, false otherwise
*/
isValid() {
const validations = validateAll(this.state.mergedSchema, this.state.properties);
const keys = Object.keys(validations);
for (const key of keys) {
if (!validations[key].valid) {
this.setState({ validations });
return false;
}
const validations = validateAll(
this.state.mergedSchema,
this.state.properties,
this.props.validation
);

const isValid = Object.keys(validations).every(key => validations[key].valid);
if (!isValid) {
this.setState({ validations });
}
return true;
return isValid;
}

/**
Expand Down Expand Up @@ -152,9 +163,17 @@ if (process.env.NODE_ENV !== 'production') {
onSubmit: PropTypes.func.isRequired,
/**
* Tigger > after callback.
* Prototype: function onTrigger(properties, fieldName, value)
* This is executed on changes on fields with uiSchema > triggers : ['after']
*/
onTrigger: PropTypes.func,
/**
* Custom validation function.
* Prototype: function validation(properties, fieldName, value)
* Return format : { valid: true|false, error: { message: 'my validation message' } }
* This is triggered on fields that has their uiSchema > customValidation : true
*/
validation: PropTypes.func,
};
}

Expand Down
33 changes: 25 additions & 8 deletions packages/forms/src/UIForm/utils/validation.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
import { validate } from 'talend-json-schema-form-core';
import { validate as staticValidate } from 'talend-json-schema-form-core';

import { getValue } from './properties';

/**
* Validate values.
* @param mergedSchema The merged schema.
* @param schema The merged schema.
* @param value The value.
* @param properties The values.
* @param customValidationFn A custom validation function
* that is applied on schema.customValidation = true
* @returns {object} The validation result.
*/
export function validate(schema, value, properties, customValidationFn) {
const staticResult = staticValidate(schema, value);
if (staticResult.valid && schema.customValidation && customValidationFn) {
return customValidationFn(properties, schema, value);
}
return staticResult;
}

/**
* Validate values.
* @param mergedSchema The merged schema array.
* @param properties The values.
* @param customValidationFn A custom validation function
* that is applied on schema.customValidation = true
* @returns {object} The validation result by field.
*/
export function validateAll(mergedSchema, properties) {
export function validateAll(mergedSchema, properties, customValidationFn) {
const validations = {};
mergedSchema.forEach((schema) => {
const { key, items } = schema;
if (key) {
validations[key] = validate(
schema,
getValue(properties, key)
);
const value = getValue(properties, key);
validations[key] = validate(schema, value, properties, customValidationFn);
}
if (items) {
const subValidations = validateAll(items, properties);
Expand All @@ -27,7 +44,7 @@ export function validateAll(mergedSchema, properties) {
}

/**
* Check if a schema value is invalid.
* Check if a schema value is valid.
* It is invalid if :
* - the schema is an invalid field (validations[key] = { valid: false })
* - the schema has items (ex: fieldset, tabs, ...), and at least one of them is invalid
Expand Down
9 changes: 9 additions & 0 deletions packages/forms/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ sampleFilenames
onTrigger={action('Trigger')}
onBlur={action('Blur')}
onSubmit={action('Submit')}
validation={(properties, schema, value) => {
action('customValidation')(properties, schema, value);
return {
valid: value.length < 5,
error: {
message: 'Custom validation : The value should be less than 5 chars',
},
};
}}
/>
</section>
));
Expand Down
32 changes: 32 additions & 0 deletions packages/forms/stories/json/core-custom-validation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"jsonSchema": {
"type": "object",
"title": "Comment",
"properties": {
"lastname": {
"type": "string"
},
"firstname": {
"type": "string"
}
},
"required": [
"lastname",
"firstname"
]
},
"uiSchema": [
{
"key": "lastname",
"title": "Last Name",
"description": "This field has custom validation (less than 5 chars)",
"customValidation": true
},
{
"key": "firstname",
"title": "First Name",
"description": "This field has no custom validation"
}
],
"properties": {}
}

0 comments on commit 1aac98a

Please sign in to comment.