@@ -129,7 +129,8 @@ export class Select extends React.Component { | |||
value: PropTypes.any, | |||
onChange: PropTypes.func, | |||
multiple: PropTypes.bool, | |||
allowEmptyValue: PropTypes.bool | |||
allowEmptyValue: PropTypes.bool, | |||
className: PropTypes.string | |||
} | |||
static defaultProps = { | |||
@@ -142,7 +143,7 @@ export class Select extends React.Component { | |||
let value | |||
if (props.value !== undefined) { | |||
if (props.value) { | |||
value = props.value | |||
} else { | |||
value = props.multiple ? [""] : "" | |||
@@ -178,7 +179,7 @@ export class Select extends React.Component { | |||
let value = this.state.value.toJS ? this.state.value.toJS() : this.state.value | |||
return ( | |||
<select multiple={ multiple } value={ value } onChange={ this.onChange } > | |||
<select className={this.props.className} multiple={ multiple } value={ value } onChange={ this.onChange } > | |||
{ allowEmptyValue ? <option value="">--</option> : null } | |||
{ | |||
allowedValues.map(function (item, key) { | |||
@@ -57,7 +57,8 @@ export class JsonSchema_string extends Component { | |||
if ( enumValue ) { | |||
const Select = getComponent("Select") | |||
return (<Select allowedValues={ enumValue } | |||
return (<Select className={ errors.length ? "invalid" : ""} | |||
allowedValues={ enumValue } | |||
value={ value } | |||
allowEmptyValue={ !required } | |||
onChange={ this.onEnumChange }/>) | |||
@@ -121,6 +122,7 @@ export class JsonSchema_array extends PureComponent { | |||
render() { | |||
let { getComponent, required, schema, fn } = this.props | |||
let errors = schema.errors || [] | |||
let itemSchema = fn.inferSchema(schema.items) | |||
const JsonSchemaForm = getComponent("JsonSchemaForm") | |||
@@ -131,19 +133,17 @@ export class JsonSchema_array extends PureComponent { | |||
if ( enumValue ) { | |||
const Select = getComponent("Select") | |||
return (<Select multiple={ true } | |||
return (<Select className={ errors.length ? "invalid" : ""} | |||
multiple={ true } | |||
value={ value } | |||
allowedValues={ enumValue } | |||
allowEmptyValue={ !required } | |||
onChange={ this.onEnumChange }/>) | |||
} | |||
let errors = schema.errors || [] | |||
return ( | |||
<div> | |||
{ !value || value.count() < 1 ? | |||
(errors.length ? <span style={{ color: "red", fortWeight: "bold" }}>{ errors[0] }</span> : null) : | |||
{ !value || value.count() < 1 ? null : | |||
value.map( (item,i) => { | |||
let schema = Object.assign({}, itemSchema) | |||
if ( errors.length ) { | |||
@@ -153,12 +153,12 @@ export class JsonSchema_array extends PureComponent { | |||
return ( | |||
<div key={i} className="json-schema-form-item"> | |||
<JsonSchemaForm fn={fn} getComponent={getComponent} value={item} onChange={(val) => this.onItemChange(val, i)} schema={schema} /> | |||
<Button className="json-schema-form-item-remove" onClick={()=> this.removeItem(i)} > - </Button> | |||
<Button className="btn btn-sm json-schema-form-item-remove" onClick={()=> this.removeItem(i)} > - </Button> | |||
</div> | |||
) | |||
}).toArray() | |||
} | |||
<Button className="json-schema-form-item-add" onClick={this.addItem}> Add item </Button> | |||
<Button className={`btn btn-sm json-schema-form-item-add ${errors.length ? "invalid" : null}`} onClick={this.addItem}> Add item </Button> | |||
</div> | |||
) | |||
} | |||
@@ -170,12 +170,14 @@ export class JsonSchema_boolean extends Component { | |||
onEnumChange = (val) => this.props.onChange(val) | |||
render() { | |||
let { getComponent, required, value } = this.props | |||
let { getComponent, value, schema } = this.props | |||
let errors = schema.errors || [] | |||
const Select = getComponent("Select") | |||
return (<Select value={ String(value) } | |||
return (<Select className={ errors.length ? "invalid" : ""} | |||
value={ String(value) } | |||
allowedValues={ fromJS(["true", "false"]) } | |||
allowEmptyValue={ !required } | |||
allowEmptyValue={ true } | |||
onChange={ this.onEnumChange }/>) | |||
} | |||
} |
@@ -468,6 +468,18 @@ export const validateFile = ( val ) => { | |||
} | |||
} | |||
export const validateBoolean = ( val ) => { | |||
if ( !(val === "true" || val === "false" || val === true || val === false) ) { | |||
return "Value must be a boolean" | |||
} | |||
} | |||
export const validateString = ( val ) => { | |||
if ( val && typeof val !== "string" ) { | |||
return "Value must be a string" | |||
} | |||
} | |||
// validation of parameters before execute | |||
export const validateParam = (param, isXml) => { | |||
let errors = [] | |||
@@ -475,53 +487,69 @@ export const validateParam = (param, isXml) => { | |||
let required = param.get("required") | |||
let type = param.get("type") | |||
let stringCheck = type === "string" && !value | |||
let arrayCheck = type === "array" && Array.isArray(value) && !value.length | |||
let listCheck = type === "array" && Im.List.isList(value) && !value.count() | |||
let fileCheck = type === "file" && !(value instanceof win.File) | |||
let nullUndefinedCheck = value === null || value === undefined | |||
if ( required && (stringCheck || arrayCheck || listCheck || fileCheck || nullUndefinedCheck) ) { | |||
errors.push("Required field is not provided") | |||
return errors | |||
} | |||
if ( value === null || value === undefined ) { | |||
return errors | |||
} | |||
if ( type === "number" ) { | |||
let err = validateNumber(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} else if ( type === "integer" ) { | |||
let err = validateInteger(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} else if ( type === "array" ) { | |||
let itemType | |||
if ( !value.count() ) { return errors } | |||
itemType = param.getIn(["items", "type"]) | |||
value.forEach((item, index) => { | |||
let err | |||
/* | |||
If the parameter is required OR the parameter has a value (meaning optional, but filled in) | |||
then we should do our validation routine. | |||
Only bother validating the parameter if the type was specified. | |||
*/ | |||
if ( type && (required || value) ) { | |||
// These checks should evaluate to true if the parameter's value is valid | |||
let stringCheck = type === "string" && value && !validateString(value) | |||
let arrayCheck = type === "array" && Array.isArray(value) && value.length | |||
let listCheck = type === "array" && Im.List.isList(value) && value.count() | |||
let fileCheck = type === "file" && value instanceof win.File | |||
let booleanCheck = type === "boolean" && !validateBoolean(value) | |||
let numberCheck = type === "number" && !validateNumber(value) // validateNumber returns undefined if the value is a number | |||
let integerCheck = type === "integer" && !validateInteger(value) // validateInteger returns undefined if the value is an integer | |||
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) { | |||
errors.push("Required field is not provided") | |||
return errors | |||
} | |||
if (itemType === "number") { | |||
err = validateNumber(item) | |||
} else if (itemType === "integer") { | |||
err = validateInteger(item) | |||
} | |||
if ( type === "string" ) { | |||
let err = validateString(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} else if ( type === "boolean" ) { | |||
let err = validateBoolean(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} else if ( type === "number" ) { | |||
let err = validateNumber(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} else if ( type === "integer" ) { | |||
let err = validateInteger(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} else if ( type === "array" ) { | |||
let itemType | |||
if ( !value.count() ) { return errors } | |||
itemType = param.getIn(["items", "type"]) | |||
value.forEach((item, index) => { | |||
let err | |||
if (itemType === "number") { | |||
err = validateNumber(item) | |||
} else if (itemType === "integer") { | |||
err = validateInteger(item) | |||
} else if (itemType === "string") { | |||
err = validateString(item) | |||
} | |||
if ( err ) { | |||
errors.push({ index: index, error: err}) | |||
} | |||
}) | |||
} else if ( type === "file" ) { | |||
let err = validateFile(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
if ( err ) { | |||
errors.push({ index: index, error: err}) | |||
} | |||
}) | |||
} else if ( type === "file" ) { | |||
let err = validateFile(value) | |||
if (!err) return errors | |||
errors.push(err) | |||
} | |||
} | |||
return errors | |||
@@ -14,6 +14,12 @@ | |||
@include text_headline(); | |||
&.btn-sm | |||
{ | |||
font-size: 12px; | |||
padding: 4px 23px; | |||
} | |||
&[disabled] | |||
{ | |||
cursor: not-allowed; | |||
@@ -165,6 +171,10 @@ | |||
button | |||
{ | |||
cursor: pointer; | |||
outline: none; | |||
&.invalid | |||
{ | |||
@include invalidFormElement(); | |||
} | |||
} |
@@ -21,6 +21,10 @@ select | |||
background: #f7f7f7; | |||
} | |||
&.invalid { | |||
@include invalidFormElement(); | |||
} | |||
} | |||
.opblock-body select | |||
@@ -55,10 +59,7 @@ input[type=file] | |||
&.invalid | |||
{ | |||
animation: shake .4s 1; | |||
border-color: $_color-delete; | |||
background: lighten($_color-delete, 35%); | |||
@include invalidFormElement(); | |||
} | |||
} | |||
@@ -166,3 +166,9 @@ $browser-context: 16; | |||
@warn 'Breakpoint mixin supports: tablet, mobile, desktop'; | |||
} | |||
} | |||
@mixin invalidFormElement() { | |||
animation: shake .4s 1; | |||
border-color: $_color-delete; | |||
background: lighten($_color-delete, 35%); | |||
} |
@@ -97,6 +97,10 @@ table | |||
width: 100%; | |||
max-width: 340px; | |||
} | |||
select { | |||
border-width: 1px; | |||
} | |||
} | |||
.parameter__name | |||
@@ -175,7 +175,19 @@ describe("utils", function() { | |||
let param = null | |||
let result = null | |||
it("skips validation when `type` is not specified", function() { | |||
// invalid type | |||
param = fromJS({ | |||
required: false, | |||
type: undefined, | |||
value: "" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates required strings", function() { | |||
// invalid string | |||
param = fromJS({ | |||
required: true, | |||
type: "string", | |||
@@ -183,9 +195,39 @@ describe("utils", function() { | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// valid string | |||
param = fromJS({ | |||
required: true, | |||
type: "string", | |||
value: "test string" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates optional strings", function() { | |||
// valid (empty) string | |||
param = fromJS({ | |||
required: false, | |||
type: "string", | |||
value: "" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// valid string | |||
param = fromJS({ | |||
required: false, | |||
type: "string", | |||
value: "test" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates required files", function() { | |||
// invalid file | |||
param = fromJS({ | |||
required: true, | |||
type: "file", | |||
@@ -193,9 +235,48 @@ describe("utils", function() { | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// valid file | |||
param = fromJS({ | |||
required: true, | |||
type: "file", | |||
value: new win.File() | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates optional files", function() { | |||
// invalid file | |||
param = fromJS({ | |||
required: false, | |||
type: "file", | |||
value: "not a file" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Value must be a file"] ) | |||
// valid (empty) file | |||
param = fromJS({ | |||
required: false, | |||
type: "file", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// valid file | |||
param = fromJS({ | |||
required: false, | |||
type: "file", | |||
value: new win.File() | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates required arrays", function() { | |||
// invalid (empty) array | |||
param = fromJS({ | |||
required: true, | |||
type: "array", | |||
@@ -204,17 +285,191 @@ describe("utils", function() { | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// invalid (not an array) | |||
param = fromJS({ | |||
required: true, | |||
type: "array", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// invalid array, items do not match correct type | |||
param = fromJS({ | |||
required: true, | |||
type: "array", | |||
value: [1], | |||
items: { | |||
type: "string" | |||
} | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [{index: 0, error: "Value must be a string"}] ) | |||
// valid array, with no 'type' for items | |||
param = fromJS({ | |||
required: true, | |||
type: "array", | |||
value: ["1"] | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// valid array, items match type | |||
param = fromJS({ | |||
required: true, | |||
type: "array", | |||
value: ["1"], | |||
items: { | |||
type: "string" | |||
} | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates optional arrays", function() { | |||
// valid, empty array | |||
param = fromJS({ | |||
required: false, | |||
type: "array", | |||
value: [] | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// invalid, items do not match correct type | |||
param = fromJS({ | |||
required: false, | |||
type: "array", | |||
value: ["number"], | |||
items: { | |||
type: "number" | |||
} | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] ) | |||
// valid | |||
param = fromJS({ | |||
required: false, | |||
type: "array", | |||
value: ["test"], | |||
items: { | |||
type: "string" | |||
} | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates required booleans", function() { | |||
// invalid boolean value | |||
param = fromJS({ | |||
required: true, | |||
type: "boolean", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// invalid boolean value (not a boolean) | |||
param = fromJS({ | |||
required: true, | |||
type: "boolean", | |||
value: "test string" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// valid boolean value | |||
param = fromJS({ | |||
required: true, | |||
type: "boolean", | |||
value: "true" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// valid boolean value | |||
param = fromJS({ | |||
required: true, | |||
type: "boolean", | |||
value: false | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates numbers", function() { | |||
// string instead of a number | |||
it("validates optional booleans", function() { | |||
// valid (empty) boolean value | |||
param = fromJS({ | |||
required: false, | |||
type: "boolean", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// invalid boolean value (not a boolean) | |||
param = fromJS({ | |||
required: false, | |||
type: "boolean", | |||
value: "test string" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Value must be a boolean"] ) | |||
// valid boolean value | |||
param = fromJS({ | |||
required: false, | |||
type: "boolean", | |||
value: "true" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// valid boolean value | |||
param = fromJS({ | |||
required: false, | |||
type: "boolean", | |||
value: false | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates required numbers", function() { | |||
// invalid number, string instead of a number | |||
param = fromJS({ | |||
required: true, | |||
type: "number", | |||
value: "test" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// invalid number, undefined value | |||
param = fromJS({ | |||
required: true, | |||
type: "number", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// valid number | |||
param = fromJS({ | |||
required: true, | |||
type: "number", | |||
value: 10 | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates optional numbers", function() { | |||
// invalid number, string instead of a number | |||
param = fromJS({ | |||
required: false, | |||
type: "number", | |||
@@ -223,7 +478,7 @@ describe("utils", function() { | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Value must be a number"] ) | |||
// undefined value | |||
// valid (empty) number | |||
param = fromJS({ | |||
required: false, | |||
type: "number", | |||
@@ -232,78 +487,72 @@ describe("utils", function() { | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
// null value | |||
// valid number | |||
param = fromJS({ | |||
required: false, | |||
type: "number", | |||
value: null | |||
value: 10 | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates integers", function() { | |||
// string instead of integer | |||
it("validates required integers", function() { | |||
// invalid integer, string instead of an integer | |||
param = fromJS({ | |||
required: false, | |||
required: true, | |||
type: "integer", | |||
value: "test" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( ["Value must be an integer"] ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// undefined value | |||
// invalid integer, undefined value | |||
param = fromJS({ | |||
required: false, | |||
required: true, | |||
type: "integer", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
expect( result ).toEqual( ["Required field is not provided"] ) | |||
// null value | |||
// valid integer | |||
param = fromJS({ | |||
required: false, | |||
required: true, | |||
type: "integer", | |||
value: null | |||
value: 10 | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
it("validates arrays", function() { | |||
// empty array | |||
it("validates optional integers", function() { | |||
// invalid integer, string instead of an integer | |||
param = fromJS({ | |||
required: false, | |||
type: "array", | |||
value: [] | |||
type: "integer", | |||
value: "test" | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [] ) | |||
expect( result ).toEqual( ["Value must be an integer"] ) | |||
// numbers | |||
// valid (empty) integer | |||
param = fromJS({ | |||
required: false, | |||
type: "array", | |||
value: ["number"], | |||
items: { | |||
type: "number" | |||
} | |||
type: "integer", | |||
value: undefined | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] ) | |||
expect( result ).toEqual( [] ) | |||
// integers | |||
param = fromJS({ | |||
required: false, | |||
type: "array", | |||
value: ["not", "numbers"], | |||
items: { | |||
type: "integer" | |||
} | |||
type: "integer", | |||
value: 10 | |||
}) | |||
result = validateParam( param, false ) | |||
expect( result ).toEqual( [{index: 0, error: "Value must be an integer"}, {index: 1, error: "Value must be an integer"}] ) | |||
expect( result ).toEqual( [] ) | |||
}) | |||
}) | |||