From 666e22f9895ec70095d4cf29ade8fd625b96b1fb Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Thu, 20 Jul 2017 21:33:57 -0600 Subject: [PATCH 1/5] Update parameter elements. Rework validateParam() function. Added .btn-sm class for "Add item" and "Remove item" buttons in array parameters. Reduce border-width on + ) @@ -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 () } } diff --git a/src/core/utils.js b/src/core/utils.js index 95771fb4..9e114dae 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -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,52 +487,66 @@ 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) + // If the parameter is required OR the parameter has a value (meaning optional, but filled in) + // then we should do our validation routine + if ( 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) ) { - errors.push("Required field is not provided") - return errors - } + if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) { + errors.push("Required field is not provided") + return errors + } - if ( value === null || value === undefined ) { - return errors - } + 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 ( 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 } - if ( !value.count() ) { return errors } + itemType = param.getIn(["items", "type"]) - itemType = param.getIn(["items", "type"]) + value.forEach((item, index) => { + let err - 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 (itemType === "number") { - err = validateNumber(item) - } else if (itemType === "integer") { - err = validateInteger(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 diff --git a/src/style/_buttons.scss b/src/style/_buttons.scss index 2470f8b0..bfb85023 100644 --- a/src/style/_buttons.scss +++ b/src/style/_buttons.scss @@ -14,6 +14,11 @@ @include text_headline(); + &.btn-sm { + font-size: 12px; + padding: 4px 23px; + } + &[disabled] { cursor: not-allowed; @@ -165,6 +170,9 @@ button { cursor: pointer; - outline: none; + + &.invalid { + @include invalidFormElement(); + } } diff --git a/src/style/_form.scss b/src/style/_form.scss index 185b836e..e54d5438 100644 --- a/src/style/_form.scss +++ b/src/style/_form.scss @@ -21,6 +21,10 @@ select background: #f7f7f7; } + + &.invalid { + @include invalidFormElement(); + } } .opblock-body select @@ -53,12 +57,8 @@ input[type=file] border-radius: 4px; background: #fff; - &.invalid - { - animation: shake .4s 1; - - border-color: $_color-delete; - background: lighten($_color-delete, 35%); + &.invalid { + @include invalidFormElement(); } } diff --git a/src/style/_mixins.scss b/src/style/_mixins.scss index a2a27d48..8fd01720 100644 --- a/src/style/_mixins.scss +++ b/src/style/_mixins.scss @@ -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%); +} diff --git a/src/style/_table.scss b/src/style/_table.scss index 3d58f3d8..d1481709 100644 --- a/src/style/_table.scss +++ b/src/style/_table.scss @@ -97,6 +97,10 @@ table width: 100%; max-width: 340px; } + + select { + border-width: 1px; + } } .parameter__name diff --git a/test/core/utils.js b/test/core/utils.js index a63da13d..65ed7435 100644 --- a/test/core/utils.js +++ b/test/core/utils.js @@ -176,6 +176,7 @@ describe("utils", function(){ let result = null it("validates required strings", function() { + // invalid string param = fromJS({ required: true, type: "string", @@ -183,9 +184,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 +224,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,75 +274,51 @@ 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: [] + value: undefined }) result = validateParam( param, false ) expect( result ).toEqual( ["Required field is not provided"] ) - }) - it("validates numbers", function() { - // string instead of a number + // invalid array, items do not match correct type param = fromJS({ - required: false, - type: "number", - value: "test" + required: true, + type: "array", + value: [1], + items: { + type: "string" + } }) result = validateParam( param, false ) - expect( result ).toEqual( ["Value must be a number"] ) + expect( result ).toEqual( [{index: 0, error: "Value must be a string"}] ) - // undefined value + // valid array, with no 'type' for items param = fromJS({ - required: false, - type: "number", - value: undefined + required: true, + type: "array", + value: ["1"] }) result = validateParam( param, false ) expect( result ).toEqual( [] ) - // null value + // valid array, items match type param = fromJS({ - required: false, - type: "number", - value: null + required: true, + type: "array", + value: ["1"], + items: { + type: "string" + } }) result = validateParam( param, false ) expect( result ).toEqual( [] ) }) - it("validates integers", function() { - // string instead of integer - param = fromJS({ - required: false, - type: "integer", - value: "test" - }) - result = validateParam( param, false ) - expect( result ).toEqual( ["Value must be an integer"] ) - - // undefined value - param = fromJS({ - required: false, - type: "integer", - value: undefined - }) - result = validateParam( param, false ) - expect( result ).toEqual( [] ) - - // null value - param = fromJS({ - required: false, - type: "integer", - value: null - }) - result = validateParam( param, false ) - expect( result ).toEqual( [] ) - }) - - it("validates arrays", function() { - // empty array + it("validates optional arrays", function() { + // valid, empty array param = fromJS({ required: false, type: "array", @@ -281,7 +327,7 @@ describe("utils", function(){ result = validateParam( param, false ) expect( result ).toEqual( [] ) - // numbers + // invalid, items do not match correct type param = fromJS({ required: false, type: "array", @@ -293,17 +339,209 @@ describe("utils", function(){ result = validateParam( param, false ) expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] ) - // integers + // valid param = fromJS({ required: false, type: "array", - value: ["not", "numbers"], + value: ["test"], items: { - type: "integer" + type: "string" } }) 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( [] ) + }) + + 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 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", + value: "test" + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Value must be a number"] ) + + // valid (empty) number + param = fromJS({ + required: false, + type: "number", + value: undefined + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) + + // valid number + param = fromJS({ + required: false, + type: "number", + value: 10 + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) + }) + + it("validates required integers", function() { + // invalid integer, string instead of an integer + param = fromJS({ + required: true, + type: "integer", + value: "test" + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Required field is not provided"] ) + + // invalid integer, undefined value + param = fromJS({ + required: true, + type: "integer", + value: undefined + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Required field is not provided"] ) + + // valid integer + param = fromJS({ + required: true, + type: "integer", + value: 10 + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) + }) + + it("validates optional integers", function() { + // invalid integer, string instead of an integer + param = fromJS({ + required: false, + type: "integer", + value: "test" + }) + result = validateParam( param, false ) + expect( result ).toEqual( ["Value must be an integer"] ) + + // valid (empty) integer + param = fromJS({ + required: false, + type: "integer", + value: undefined + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) + + // valid number + param = fromJS({ + required: false, + type: "integer", + value: 10 + }) + result = validateParam( param, false ) + expect( result ).toEqual( [] ) }) }) }) From c82f85f1234cf6fd0c08a161c056e865c9780e1c Mon Sep 17 00:00:00 2001 From: shockey Date: Fri, 21 Jul 2017 16:01:52 -0700 Subject: [PATCH 2/5] Update topbar.jsx --- src/plugins/topbar/topbar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/topbar/topbar.jsx b/src/plugins/topbar/topbar.jsx index fd3654bb..ec9db730 100644 --- a/src/plugins/topbar/topbar.jsx +++ b/src/plugins/topbar/topbar.jsx @@ -130,7 +130,7 @@ export default class Topbar extends React.Component {
- Swagger UX + Swagger UI swagger
From 0010bf20a62f37d8284211471d37c3936df10606 Mon Sep 17 00:00:00 2001 From: shockey Date: Fri, 21 Jul 2017 16:04:58 -0700 Subject: [PATCH 3/5] Update add-plugin.md --- src/plugins/add-plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/add-plugin.md b/src/plugins/add-plugin.md index 7a77cb30..2e2d52e1 100644 --- a/src/plugins/add-plugin.md +++ b/src/plugins/add-plugin.md @@ -20,7 +20,7 @@ SwaggerUI({ }) ``` -Or if you're updating the core plugins.. you'll add it to [src/js/bootstrap-plugin](https://github.com/SmartBear/swagger-ux/blob/master/src/js/bootstrap-plugin.js) +Or if you're updating the core plugins.. you'll add it to the base preset: [src/core/presets/base.js](https://github.com/swagger-api/swagger-ui/blob/master/src/core/presets/base.js) Each Plugin is a function that returns an object. That object will get merged with the `system` and later bound to the state. Here is an example of each `type` From eb1f3b1fa613d68e18c965d1cb48d6f886d01f39 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Fri, 21 Jul 2017 17:23:28 -0600 Subject: [PATCH 4/5] Fixes #3422 - Fixed regression bug on models.jsx expand/collapse arrows due to bad merge --- src/core/components/models.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/components/models.jsx b/src/core/components/models.jsx index c61ae0c9..86c3256d 100644 --- a/src/core/components/models.jsx +++ b/src/core/components/models.jsx @@ -24,8 +24,8 @@ export default class Models extends Component { return

layoutActions.show("models", !showModels)}> Models - - + +

From b9bb4c124ec76bc3e565344c8abb5ce26432e9f6 Mon Sep 17 00:00:00 2001 From: Owen Conti Date: Fri, 21 Jul 2017 18:46:50 -0600 Subject: [PATCH 5/5] Fixes #3434 - Change link style color for .response-col_description__inner links --- src/style/_layout.scss | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/style/_layout.scss b/src/style/_layout.scss index 90b9a088..43000ec6 100644 --- a/src/style/_layout.scss +++ b/src/style/_layout.scss @@ -508,6 +508,15 @@ body { margin: 0; } + + a + { + @include text_code(#89bf04); + text-decoration: underline; + &:hover { + color: #81b10c; + } + } } }