Repository: mozilla-services/react-jsonschema-form
Pull request #842 (2018-02-15 06:48:20)

Fix warning on FileWidget
### Reasons for making this change
Following warning is thrown when using FileWidget:

`Warning: Setting defaultProps as an instance property on FileWidget is not supported and will be ignored. Instead, define defaultProps as a static property on FileWidget.`

It seems like default props have been set twice on the FileWidget so I just removed the instance that is causing the warnings. I checked and there are no other instances of this problem.

### Checklist

* [ ] **I'm updating documentation**
  - [ ] I've checked the rendering of the Markdown text I've added
  - [ ] If I'm adding a new section, I've updated the Table of Content
* [ ] **I'm adding or updating code**
  - [ ] I've added and/or updated tests
  - [ ] I've updated docs if needed
  - [ ] I've run `npm run cs-format` on my branch to conform my code to [prettier](https://github.com/prettier/prettier) coding style
* [ ] **I'm adding a new feature**
  - [ ] I've updated the playground with an example use of the feature



Patch:
diff --git a/README.md b/README.md
index 1d4ea3050..02b5e8598 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ react-jsonschema-form
 
 [![Build Status](https://travis-ci.org/mozilla-services/react-jsonschema-form.svg)](https://travis-ci.org/mozilla-services/react-jsonschema-form)
 
-A simple [React](http://facebook.github.io/react/) component capable of building HTML forms out of a [JSON schema](http://jsonschema.net/) and using [Bootstrap](http://getbootstrap.com/) semantics by default.
+A simple [React](http://facebook.github.io/react/) component capable of building HTML forms out of a [JSON schema](http://json-schema.org/) and using [Bootstrap](http://getbootstrap.com/) semantics by default.
 
 A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) is hosted on gh-pages.
 
@@ -22,6 +22,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
         - [Form error event handler](#form-error-event-handler)
         - [Form data changes](#form-data-changes)
         - [Form field blur events](#form-field-blur-events)
+        - [Form field focus events](#form-field-focus-events)
   - [Form customization](#form-customization)
      - [The uiSchema object](#the-uischema-object)
      - [Alternative widgets](#alternative-widgets)
@@ -42,7 +43,8 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
         - [removable option](#removable-option)
      - [Custom CSS class names](#custom-css-class-names)
      - [Custom labels for enum fields](#custom-labels-for-enum-fields)
-     - [Disabled enum fields](#disabled-attribute-for-enum-fields)
+        - [Alternative JSON-Schema compliant approach](#alternative-json-schema-compliant-approach)
+     - [Disabled attribute for enum fields](#disabled-attribute-for-enum-fields)
      - [Multiple choices list](#multiple-choices-list)
      - [Autogenerated widget ids](#autogenerated-widget-ids)
      - [Form action buttons](#form-action-buttons)
@@ -60,11 +62,12 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
      - [Array Field Template](#array-field-template)
      - [Object Field Template](#object-field-template)
      - [Error List template](#error-list-template)
+     - [Id prefix](#id-prefix)
      - [Custom widgets and fields](#custom-widgets-and-fields)
      - [Custom widget components](#custom-widget-components)
         - [Custom component registration](#custom-component-registration)
         - [Custom widget options](#custom-widget-options)
-        - [Customizing widgets' text input](#customizing-widgets-text-input)
+        - [Customizing widgets text input](#customizing-widgets-text-input)
      - [Custom field components](#custom-field-components)
         - [Field props](#field-props)
         - [The registry object](#the-registry-object)
@@ -84,11 +87,11 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
   - [Styling your forms](#styling-your-forms)
   - [Schema definitions and references](#schema-definitions-and-references)
   - [Property dependencies](#property-dependencies)
-    - [Unidirectional](#unidirectional)
-    - [Bidirectional](#bidirectional)
+     - [Unidirectional](#unidirectional)
+     - [Bidirectional](#bidirectional)
   - [Schema dependencies](#schema-dependencies)
-    - [Conditional](#conditional)
-    - [Dynamic](#dynamic)
+     - [Conditional](#conditional)
+     - [Dynamic](#dynamic)
   - [JSON Schema supporting status](#json-schema-supporting-status)
   - [Tips and tricks](#tips-and-tricks)
   - [Contributing](#contributing)
@@ -96,7 +99,10 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
      - [Development server](#development-server)
      - [Tests](#tests)
         - [TDD](#tdd)
+     - [Releasing](#releasing)
   - [FAQ](#faq)
+     - [Q: Does rjsf support oneOf, anyOf, multiple types in an array, etc.?](#q-does-rjsf-support-oneof-anyof-multiple-types-in-an-array-etc)
+     - [Q: Will react-jsonschema-form support Material, Ant-Design, Foundation, or [some other specific widget library or frontend style]?](#q-will-react-jsonschema-form-support-material-ant-design-foundation-or-some-other-specific-widget-library-or-frontend-style)
   - [License](#license)
 
 ---
@@ -559,6 +565,51 @@ This will be rendered using a select box that way:
 
 Note that string representations of numbers will be cast back and reflected as actual numbers into form state.
 
+#### Alternative JSON-Schema compliant approach
+
+The JSON Schema team concluded an alternative approach instead of enumNames and react-jsonschema-form supports it as well.
+
+```js
+const schema = {
+  "type": "number",
+  "anyOf": [
+    {
+      "type": "number",
+      "title": "one",
+      "enum": [
+        1
+      ]
+    },
+    {
+      "type": "number",
+      "title": "two",
+      "enum": [
+        2
+      ]
+    },
+    {
+      "type": "number",
+      "title": "three",
+      "enum": [
+        3
+      ]
+    }
+  ]
+};
+```
+
+As above this will be rendered using a select box as so:
+
+```html
+<select>
+  <option value="1">one</option>
+  <option value="2">two</option>
+  <option value="3">three</option>
+</select>
+```
+
+A live example of both approaches side-by-side can be found in the **Alternatives** playground preset.
+
 ### Disabled attribute for `enum` fields
 
 This library supports the 'disabled' attribute for `enum` options. Enum disabled allows disabling options for 'enum' fields.This attribute can be added as a part of uiSchema.
@@ -1139,7 +1190,7 @@ render((
 
 > Note: Since v0.41.0, the `ui:widget` object API, where a widget and options were specified with `"ui:widget": {component, options}` shape, is deprecated. It will be removed in a future release.
 
-#### Customizing widgets' text input
+#### Customizing widgets text input
 
 All the widgets that render a text input use the `BaseInput` component internally. If you need to customize all text inputs without customizing all widgets individially, you can provide a `BaseInput` component in the `widgets` property of `Form` (see [Custom component registration](#custom-component-registration).
 
diff --git a/package.json b/package.json
index ed56ee081..0137af3c3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "react-jsonschema-form",
-  "version": "1.0.1",
+  "version": "1.0.2",
   "description": "A simple React component capable of building HTML forms out of a JSON schema.",
   "scripts": {
     "build:readme": "toctoc README.md -w",
@@ -8,17 +8,18 @@
     "build:dist": "rimraf dist && cross-env NODE_ENV=production webpack --config webpack.config.dist.js --optimize-minimize",
     "build:playground": "rimraf build && cross-env NODE_ENV=production webpack --config webpack.config.prod.js --optimize-minimize && cp playground/index.prod.html build/index.html",
     "cs-check": "prettier -l $npm_package_prettierOptions '{playground,src,test}/**/*.js'",
-    "cs-format": "prettier $npm_package_prettierOptions '{playground,src,test}/**/*.js' --write",
+    "cs-format": "prettier --jsx-bracket-same-line --trailing-comma es5 --use-tabs false --semi --tab-width 2 '{playground,src,test}/**/*.js' --write",
     "dist": "npm run build:lib && npm run build:dist",
     "lint": "eslint src test playground",
     "precommit": "lint-staged",
     "publish-to-gh-pages": "npm run build:playground && gh-pages --dist build/",
     "publish-to-npm": "npm run build:readme && npm run dist && npm publish",
+    "preversion": "npm run build:playground && npm run dist && npm run build:readme && npm run cs-check && npm run lint",
     "start": "node devServer.js",
     "tdd": "cross-env NODE_ENV=test mocha --compilers js:babel-register --watch --require ./test/setup-jsdom.js test/**/*_test.js",
     "test": "cross-env NODE_ENV=test mocha --compilers js:babel-register --require ./test/setup-jsdom.js test/**/*_test.js"
   },
-  "prettierOptions": "--jsx-bracket-same-line --trailing-comma es5 --semi",
+  "prettierOptions": "--jsx-bracket-same-line --trailing-comma es5 --semi --tab-width 2",
   "lint-staged": {
     "{playground,src,test}/**/*.js": [
       "npm run lint",
@@ -81,7 +82,7 @@
     "prettier": "^1.8.2",
     "react": "^15.5.0",
     "react-addons-test-utils": "^15.3.2",
-    "react-codemirror2": "^2.0.2",
+    "react-codemirror2": "^4.1.0",
     "react-dom": "^15.3.2",
     "react-transform-catch-errors": "^1.0.0",
     "react-transform-hmr": "^1.0.1",
diff --git a/playground/app.js b/playground/app.js
index e7fd1ccc8..5b30930bc 100644
--- a/playground/app.js
+++ b/playground/app.js
@@ -1,6 +1,6 @@
 import React, { Component } from "react";
 import { render } from "react-dom";
-import CodeMirror from "react-codemirror2";
+import { UnControlled as CodeMirror } from "react-codemirror2";
 import "codemirror/mode/javascript/javascript";
 
 import { shouldRender } from "../src/utils";
@@ -213,6 +213,7 @@ class Editor extends Component {
         <CodeMirror
           value={this.state.code}
           onChange={this.onCodeChange}
+          autoCursor={false}
           options={Object.assign({}, cmOptions, { theme })}
         />
       </div>
diff --git a/src/components/Form.js b/src/components/Form.js
index e32a7b239..a0061609e 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -70,11 +70,13 @@ export default class Form extends Component {
     return shouldRender(this, nextProps, nextState);
   }
 
-  validate(formData, schema) {
+  validate(formData, schema = this.props.schema) {
     const { validate, transformErrors } = this.props;
+    const { definitions } = this.getRegistry();
+    const resolvedSchema = retrieveSchema(schema, definitions, formData);
     return validateFormData(
       formData,
-      schema || this.props.schema,
+      resolvedSchema,
       validate,
       transformErrors
     );
@@ -173,6 +175,7 @@ export default class Form extends Component {
       children,
       safeRenderCompletion,
       id,
+      idPrefix,
       className,
       name,
       method,
@@ -207,6 +210,7 @@ export default class Form extends Component {
           uiSchema={uiSchema}
           errorSchema={errorSchema}
           idSchema={idSchema}
+          idPrefix={idPrefix}
           formData={formData}
           onChange={this.onChange}
           onBlur={this.onBlur}
diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js
index 11e523693..dd71f8564 100644
--- a/src/components/fields/ArrayField.js
+++ b/src/components/fields/ArrayField.js
@@ -64,7 +64,10 @@ function DefaultArrayItem(props) {
         <div className="col-xs-3 array-item-toolbox">
           <div
             className="btn-group"
-            style={{ display: "flex", justifyContent: "space-around" }}>
+            style={{
+              display: "flex",
+              justifyContent: "space-around",
+            }}>
             {(props.hasMoveUp || props.hasMoveDown) && (
               <IconBtn
                 icon="arrow-up"
@@ -363,13 +366,13 @@ class ArrayField extends Component {
       readonly,
       autofocus,
       registry = getDefaultRegistry(),
-      formContext,
       onBlur,
       onFocus,
       idPrefix,
+      rawErrors,
     } = this.props;
     const title = schema.title === undefined ? name : schema.title;
-    const { ArrayFieldTemplate, definitions, fields } = registry;
+    const { ArrayFieldTemplate, definitions, fields, formContext } = registry;
     const { TitleField, DescriptionField } = fields;
     const itemsSchema = retrieveSchema(schema.items, definitions);
     const arrayProps = {
@@ -412,6 +415,7 @@ class ArrayField extends Component {
       TitleField,
       formContext,
       formData,
+      rawErrors,
     };
 
     // Check if a custom render function was passed in
@@ -431,6 +435,7 @@ class ArrayField extends Component {
       onBlur,
       onFocus,
       registry = getDefaultRegistry(),
+      rawErrors,
     } = this.props;
     const items = this.props.formData;
     const { widgets, definitions, formContext } = registry;
@@ -455,6 +460,7 @@ class ArrayField extends Component {
         readonly={readonly}
         formContext={formContext}
         autofocus={autofocus}
+        rawErrors={rawErrors}
       />
     );
   }
@@ -471,6 +477,7 @@ class ArrayField extends Component {
       onBlur,
       onFocus,
       registry = getDefaultRegistry(),
+      rawErrors,
     } = this.props;
     const title = schema.title || name;
     const items = this.props.formData;
@@ -492,6 +499,7 @@ class ArrayField extends Component {
         readonly={readonly}
         formContext={formContext}
         autofocus={autofocus}
+        rawErrors={rawErrors}
       />
     );
   }
@@ -512,10 +520,11 @@ class ArrayField extends Component {
       registry = getDefaultRegistry(),
       onBlur,
       onFocus,
+      rawErrors,
     } = this.props;
     const title = schema.title || name;
     let items = this.props.formData;
-    const { ArrayFieldTemplate, definitions, fields } = registry;
+    const { ArrayFieldTemplate, definitions, fields, formContext } = registry;
     const { TitleField } = fields;
     const itemSchemas = schema.items.map((item, index) =>
       retrieveSchema(item, definitions, formData[index])
@@ -579,6 +588,8 @@ class ArrayField extends Component {
       uiSchema,
       title,
       TitleField,
+      formContext,
+      rawErrors,
     };
 
     // Check if a custom template template was passed in
@@ -600,6 +611,7 @@ class ArrayField extends Component {
       autofocus,
       onBlur,
       onFocus,
+      rawErrors,
     } = props;
     const {
       disabled,
@@ -636,6 +648,7 @@ class ArrayField extends Component {
           disabled={this.props.disabled}
           readonly={this.props.readonly}
           autofocus={autofocus}
+          rawErrors={rawErrors}
         />
       ),
       className: "array-item",
diff --git a/src/components/fields/ObjectField.js b/src/components/fields/ObjectField.js
index 7945d251c..b6908ae00 100644
--- a/src/components/fields/ObjectField.js
+++ b/src/components/fields/ObjectField.js
@@ -73,6 +73,7 @@ class ObjectField extends Component {
       required,
       disabled,
       readonly,
+      idPrefix,
       onBlur,
       onFocus,
       registry = getDefaultRegistry(),
@@ -117,6 +118,7 @@ class ObjectField extends Component {
               uiSchema={uiSchema[name]}
               errorSchema={errorSchema[name]}
               idSchema={idSchema[name]}
+              idPrefix={idPrefix}
               formData={formData[name]}
               onChange={this.onPropertyChange(name)}
               onBlur={onBlur}
diff --git a/src/components/fields/SchemaField.js b/src/components/fields/SchemaField.js
index 105c7f325..ecbacdf0b 100644
--- a/src/components/fields/SchemaField.js
+++ b/src/components/fields/SchemaField.js
@@ -4,7 +4,9 @@ import PropTypes from "prop-types";
 import {
   isMultiSelect,
   retrieveSchema,
+  toIdSchema,
   getDefaultRegistry,
+  mergeObjects,
   getUiOptions,
   isFilesArray,
   deepEquals,
@@ -153,7 +155,7 @@ function SchemaFieldRender(props) {
     uiSchema,
     formData,
     errorSchema,
-    idSchema,
+    idPrefix,
     name,
     required,
     registry = getDefaultRegistry(),
@@ -164,7 +166,12 @@ function SchemaFieldRender(props) {
     formContext,
     FieldTemplate = DefaultTemplate,
   } = registry;
+  let idSchema = props.idSchema;
   const schema = retrieveSchema(props.schema, definitions, formData);
+  idSchema = mergeObjects(
+    toIdSchema(schema, null, definitions, formData, idPrefix),
+    idSchema
+  );
   const FieldComponent = getFieldComponent(schema, uiSchema, idSchema, fields);
   const { DescriptionField } = fields;
   const disabled = Boolean(props.disabled || uiSchema["ui:disabled"]);
@@ -199,6 +206,7 @@ function SchemaFieldRender(props) {
   const field = (
     <FieldComponent
       {...props}
+      idSchema={idSchema}
       schema={schema}
       uiSchema={{ ...uiSchema, classNames: undefined }}
       disabled={disabled}
diff --git a/src/components/widgets/BaseInput.js b/src/components/widgets/BaseInput.js
index 9a39e2967..70801271b 100644
--- a/src/components/widgets/BaseInput.js
+++ b/src/components/widgets/BaseInput.js
@@ -4,6 +4,10 @@ import PropTypes from "prop-types";
 function BaseInput(props) {
   // Note: since React 15.2.0 we can't forward unknown element attributes, so we
   // exclude the "options" and "schema" ones here.
+  if (!props.id) {
+    console.log("No id for", props);
+    throw new Error(`no id for props ${JSON.stringify(props)}`);
+  }
   const {
     value,
     readonly,
diff --git a/src/components/widgets/FileWidget.js b/src/components/widgets/FileWidget.js
index ebde27422..916bbea38 100644
--- a/src/components/widgets/FileWidget.js
+++ b/src/components/widgets/FileWidget.js
@@ -60,10 +60,6 @@ function extractFileInfo(dataURLs) {
 }
 
 class FileWidget extends Component {
-  defaultProps = {
-    multiple: false,
-  };
-
   constructor(props) {
     super(props);
     const { value } = props;
diff --git a/src/validate.js b/src/validate.js
index 1f72b8dcd..2c2c37353 100644
--- a/src/validate.js
+++ b/src/validate.js
@@ -153,7 +153,12 @@ export default function validateFormData(
   customValidate,
   transformErrors
 ) {
-  ajv.validate(schema, formData);
+  try {
+    ajv.validate(schema, formData);
+  } catch (e) {
+    // swallow errors thrown in ajv due to invalid schemas, these
+    // still get displayed
+  }
 
   let errors = transformAjvErrors(ajv.errors);
 


Test patch:
diff --git a/test/ArrayField_test.js b/test/ArrayField_test.js
index 9dc22e21c..633c57347 100644
--- a/test/ArrayField_test.js
+++ b/test/ArrayField_test.js
@@ -7,7 +7,9 @@ import { createFormComponent, createSandbox } from "./test_utils";
 
 describe("ArrayField", () => {
   let sandbox;
-  const CustomComponent = () => <div id="custom" />;
+  const CustomComponent = props => {
+    return <div id="custom">{props.rawErrors}</div>;
+  };
 
   beforeEach(() => {
     sandbox = createSandbox();
@@ -98,6 +100,29 @@ describe("ArrayField", () => {
       expect(node.querySelector("#custom")).to.exist;
     });
 
+    it("should pass rawErrors down to custom array field templates", () => {
+      const schema = {
+        type: "array",
+        title: "my list",
+        description: "my description",
+        items: { type: "string" },
+        minItems: 2,
+      };
+
+      const { node } = createFormComponent({
+        schema,
+        ArrayFieldTemplate: CustomComponent,
+        formData: [1],
+        liveValidate: true,
+      });
+
+      const matches = node.querySelectorAll("#custom");
+      expect(matches).to.have.length.of(1);
+      expect(matches[0].textContent).to.eql(
+        "should NOT have less than 2 items"
+      );
+    });
+
     it("should contain no field in the list by default", () => {
       const { node } = createFormComponent({ schema });
 
@@ -410,7 +435,10 @@ describe("ArrayField", () => {
           },
         },
       };
-      let form = createFormComponent({ schema: complexSchema, formData: {} });
+      let form = createFormComponent({
+        schema: complexSchema,
+        formData: {},
+      });
       let inputs = form.node.querySelectorAll("input[type=text]");
       expect(inputs[0].value).eql("Default name");
       expect(inputs[1].value).eql("Default name");
@@ -660,6 +688,23 @@ describe("ArrayField", () => {
 
         expect(node.querySelector("select").id).eql("root");
       });
+
+      it("should pass rawErrors down to custom widgets", () => {
+        const { node } = createFormComponent({
+          schema,
+          widgets: {
+            SelectWidget: CustomComponent,
+          },
+          formData: ["foo", "foo"],
+          liveValidate: true,
+        });
+
+        const matches = node.querySelectorAll("#custom");
+        expect(matches).to.have.length.of(1);
+        expect(matches[0].textContent).to.eql(
+          "should NOT have duplicate items (items ## 0 and 1 are identical)"
+        );
+      });
     });
 
     describe("CheckboxesWidget", () => {
@@ -684,7 +729,10 @@ describe("ArrayField", () => {
       });
 
       it("should handle a change event", () => {
-        const { comp, node } = createFormComponent({ schema, uiSchema });
+        const { comp, node } = createFormComponent({
+          schema,
+          uiSchema,
+        });
 
         Simulate.change(node.querySelectorAll("[type=checkbox]")[0], {
           target: { checked: true },
@@ -729,6 +777,35 @@ describe("ArrayField", () => {
 
         expect(node.querySelectorAll(".checkbox-inline")).to.have.length.of(3);
       });
+
+      it("should pass rawErrors down to custom widgets", () => {
+        const schema = {
+          type: "array",
+          title: "My field",
+          items: {
+            enum: ["foo", "bar", "fuzz"],
+            type: "string",
+          },
+          minItems: 3,
+          uniqueItems: true,
+        };
+
+        const { node } = createFormComponent({
+          schema,
+          widgets: {
+            CheckboxesWidget: CustomComponent,
+          },
+          uiSchema,
+          formData: [],
+          liveValidate: true,
+        });
+
+        const matches = node.querySelectorAll("#custom");
+        expect(matches).to.have.length.of(1);
+        expect(matches[0].textContent).to.eql(
+          "should NOT have less than 3 items"
+        );
+      });
     });
   });
 
@@ -809,6 +886,33 @@ describe("ArrayField", () => {
 
       expect(node.querySelector("input[type=file]").id).eql("root");
     });
+
+    it("should pass rawErrors down to custom widgets", () => {
+      const schema = {
+        type: "array",
+        title: "My field",
+        items: {
+          type: "string",
+          format: "data-url",
+        },
+        minItems: 5,
+      };
+
+      const { node } = createFormComponent({
+        schema,
+        widgets: {
+          FileWidget: CustomComponent,
+        },
+        formData: [],
+        liveValidate: true,
+      });
+
+      const matches = node.querySelectorAll("#custom");
+      expect(matches).to.have.length.of(1);
+      expect(matches[0].textContent).to.eql(
+        "should NOT have less than 5 items"
+      );
+    });
   });
 
   describe("Nested lists", () => {
@@ -840,6 +944,51 @@ describe("ArrayField", () => {
 
       expect(node.querySelectorAll("fieldset fieldset")).to.have.length.of(1);
     });
+
+    it("should pass rawErrors down to every level of custom widgets", () => {
+      const CustomItem = props => <div id="custom-item">{props.children}</div>;
+      const CustomTemplate = props => {
+        return (
+          <div id="custom">
+            {props.items &&
+              props.items.map((p, i) => <CustomItem key={i} {...p} />)}
+            <div id="custom-error">
+              {props.rawErrors && props.rawErrors.join(", ")}
+            </div>
+          </div>
+        );
+      };
+
+      const schema = {
+        type: "array",
+        title: "A list of arrays",
+        items: {
+          type: "array",
+          title: "A list of numbers",
+          items: {
+            type: "number",
+          },
+          minItems: 3,
+        },
+        minItems: 2,
+      };
+
+      const { node } = createFormComponent({
+        schema,
+        ArrayFieldTemplate: CustomTemplate,
+        formData: [[]],
+        liveValidate: true,
+      });
+
+      const matches = node.querySelectorAll("#custom-error");
+      expect(matches).to.have.length.of(2);
+      expect(matches[0].textContent).to.eql(
+        "should NOT have less than 3 items"
+      );
+      expect(matches[1].textContent).to.eql(
+        "should NOT have less than 2 items"
+      );
+    });
   });
 
   describe("Fixed items lists", () => {
@@ -915,7 +1064,10 @@ describe("ArrayField", () => {
     });
 
     it("should fill fields with data", () => {
-      const { node } = createFormComponent({ schema, formData: ["foo", 42] });
+      const { node } = createFormComponent({
+        schema,
+        formData: ["foo", 42],
+      });
       const strInput = node.querySelector(
         "fieldset .field-string input[type=text]"
       );
diff --git a/test/Form_test.js b/test/Form_test.js
index ec2658394..db1307a2b 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -68,6 +68,85 @@ describe("Form", () => {
     });
   });
 
+  describe("Changing idPrefix", function() {
+    it("should work with simple example", function() {
+      const schema = {
+        type: "object",
+        title: "root object",
+        required: ["foo"],
+        properties: {
+          count: {
+            type: "number",
+          },
+        },
+      };
+      const comp = renderIntoDocument(<Form schema={schema} idPrefix="rjsf" />);
+      const node = findDOMNode(comp);
+      const inputs = node.querySelectorAll("input");
+      const ids = [];
+      for (var i = 0, len = inputs.length; i < len; i++) {
+        const input = inputs[i];
+        ids.push(input.getAttribute("id"));
+      }
+      expect(ids).to.eql(["rjsf_count"]);
+    });
+
+    it("should work with oneOf", function() {
+      const schema = {
+        $schema: "http://json-schema.org/draft-06/schema#",
+        type: "object",
+        properties: {
+          connector: {
+            type: "string",
+            enum: ["aws", "gcp"],
+            title: "Provider",
+            default: "aws",
+          },
+        },
+        dependencies: {
+          connector: {
+            oneOf: [
+              {
+                type: "object",
+                properties: {
+                  connector: {
+                    type: "string",
+                    enum: ["aws"],
+                  },
+                  key_aws: {
+                    type: "string",
+                  },
+                },
+              },
+              {
+                type: "object",
+                properties: {
+                  connector: {
+                    type: "string",
+                    enum: ["gcp"],
+                  },
+                  key_gcp: {
+                    type: "string",
+                  },
+                },
+              },
+            ],
+          },
+        },
+      };
+
+      const comp = renderIntoDocument(<Form schema={schema} idPrefix="rjsf" />);
+      const node = findDOMNode(comp);
+      const inputs = node.querySelectorAll("input");
+      const ids = [];
+      for (var i = 0, len = inputs.length; i < len; i++) {
+        const input = inputs[i];
+        ids.push(input.getAttribute("id"));
+      }
+      expect(ids).to.eql(["rjsf_key_aws"]);
+    });
+  });
+
   describe("Custom field template", () => {
     const schema = {
       type: "object",
@@ -592,7 +671,11 @@ describe("Form", () => {
         foo: "",
       };
       const onChange = sandbox.spy();
-      const { node } = createFormComponent({ schema, formData, onChange });
+      const { node } = createFormComponent({
+        schema,
+        formData,
+        onChange,
+      });
 
       Simulate.change(node.querySelector("[type=text]"), {
         target: { value: "new" },
@@ -835,7 +918,10 @@ describe("Form", () => {
         });
 
         it("should denote the new error in the field", () => {
-          const { node } = createFormComponent({ schema, liveValidate: true });
+          const { node } = createFormComponent({
+            schema,
+            liveValidate: true,
+          });
 
           Simulate.change(node.querySelector("input[type=text]"), {
             target: { value: "short" },
@@ -1056,7 +1142,9 @@ describe("Form", () => {
         const { comp } = createFormComponent(formProps);
 
         expect(comp.state.errorSchema).eql({
-          1: { __errors: ["should NOT be shorter than 4 characters"] },
+          1: {
+            __errors: ["should NOT be shorter than 4 characters"],
+          },
         });
       });
 
@@ -1108,8 +1196,12 @@ describe("Form", () => {
 
         expect(comp.state.errorSchema).eql({
           level1: {
-            1: { __errors: ["should NOT be shorter than 4 characters"] },
-            3: { __errors: ["should NOT be shorter than 4 characters"] },
+            1: {
+              __errors: ["should NOT be shorter than 4 characters"],
+            },
+            3: {
+              __errors: ["should NOT be shorter than 4 characters"],
+            },
           },
         });
       });
@@ -1158,10 +1250,14 @@ describe("Form", () => {
         expect(comp.state.errorSchema).eql({
           outer: {
             0: {
-              1: { __errors: ["should NOT be shorter than 4 characters"] },
+              1: {
+                __errors: ["should NOT be shorter than 4 characters"],
+              },
             },
             1: {
-              0: { __errors: ["should NOT be shorter than 4 characters"] },
+              0: {
+                __errors: ["should NOT be shorter than 4 characters"],
+              },
             },
           },
         });
@@ -1229,6 +1325,103 @@ describe("Form", () => {
         expect(errors).eql(["should NOT be shorter than 4 characters"]);
       });
     });
+
+    describe("schema dependencies", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          branch: {
+            type: "number",
+            enum: [1, 2, 3],
+            default: 1,
+          },
+        },
+        required: ["branch"],
+        dependencies: {
+          branch: {
+            oneOf: [
+              {
+                properties: {
+                  branch: {
+                    enum: [1],
+                  },
+                  field1: {
+                    type: "number",
+                  },
+                },
+                required: ["field1"],
+              },
+              {
+                properties: {
+                  branch: {
+                    enum: [2],
+                  },
+                  field1: {
+                    type: "number",
+                  },
+                  field2: {
+                    type: "number",
+                  },
+                },
+                required: ["field1", "field2"],
+              },
+            ],
+          },
+        },
+      };
+
+      it("should only show error for property in selected branch", () => {
+        const { comp, node } = createFormComponent({
+          schema,
+          liveValidate: true,
+        });
+
+        Simulate.change(node.querySelector("input[type=text]"), {
+          target: { value: "not a number" },
+        });
+
+        expect(comp.state.errorSchema).eql({
+          field1: {
+            __errors: ["should be number"],
+          },
+        });
+      });
+
+      it("should only show errors for properties in selected branch", () => {
+        const { comp, node } = createFormComponent({
+          schema,
+          liveValidate: true,
+          formData: { branch: 2 },
+        });
+
+        Simulate.change(node.querySelector("input[type=text]"), {
+          target: { value: "not a number" },
+        });
+
+        expect(comp.state.errorSchema).eql({
+          field1: {
+            __errors: ["should be number"],
+          },
+          field2: {
+            __errors: ["is a required property"],
+          },
+        });
+      });
+
+      it("should not show any errors when branch is empty", () => {
+        const { comp, node } = createFormComponent({
+          schema,
+          liveValidate: true,
+          formData: { branch: 3 },
+        });
+
+        Simulate.change(node.querySelector("select"), {
+          target: { value: 3 },
+        });
+
+        expect(comp.state.errorSchema).eql({});
+      });
+    });
   });
 
   describe("Schema and formData updates", () => {
diff --git a/test/NumberField_test.js b/test/NumberField_test.js
index b5eee0fe3..f1f5fd482 100644
--- a/test/NumberField_test.js
+++ b/test/NumberField_test.js
@@ -51,7 +51,9 @@ describe("NumberField", () => {
     });
 
     it("should default state value to undefined", () => {
-      const { comp } = createFormComponent({ schema: { type: "number" } });
+      const { comp } = createFormComponent({
+        schema: { type: "number" },
+      });
 
       expect(comp.state.formData).eql(undefined);
     });
diff --git a/test/SchemaField_test.js b/test/SchemaField_test.js
index 6376b4fd5..fa503d767 100644
--- a/test/SchemaField_test.js
+++ b/test/SchemaField_test.js
@@ -22,7 +22,9 @@ describe("SchemaField", () => {
 
   describe("Unsupported field", () => {
     it("should warn on invalid field type", () => {
-      const { node } = createFormComponent({ schema: { type: "invalid" } });
+      const { node } = createFormComponent({
+        schema: { type: "invalid" },
+      });
 
       expect(node.querySelector(".unsupported-field").textContent).to.contain(
         "Unknown field type invalid"
@@ -240,7 +242,9 @@ describe("SchemaField", () => {
           },
         },
       };
-      const { node } = createFormComponent({ schema: schemaWithReference });
+      const { node } = createFormComponent({
+        schema: schemaWithReference,
+      });
 
       const matches = node.querySelectorAll("#root_foo__description");
       expect(matches).to.have.length.of(1);
@@ -301,7 +305,11 @@ describe("SchemaField", () => {
     }
 
     it("should render it's own errors", () => {
-      const { node } = createFormComponent({ schema, uiSchema, validate });
+      const { node } = createFormComponent({
+        schema,
+        uiSchema,
+        validate,
+      });
       submit(node);
 
       const matches = node.querySelectorAll(
@@ -312,7 +320,11 @@ describe("SchemaField", () => {
     });
 
     it("should pass errors to child component", () => {
-      const { node } = createFormComponent({ schema, uiSchema, validate });
+      const { node } = createFormComponent({
+        schema,
+        uiSchema,
+        validate,
+      });
       submit(node);
 
       const matches = node.querySelectorAll(
diff --git a/test/StringField_test.js b/test/StringField_test.js
index af02df60b..8bb9d2b07 100644
--- a/test/StringField_test.js
+++ b/test/StringField_test.js
@@ -66,7 +66,9 @@ describe("StringField", () => {
     });
 
     it("should default state value to undefined", () => {
-      const { comp } = createFormComponent({ schema: { type: "string" } });
+      const { comp } = createFormComponent({
+        schema: { type: "string" },
+      });
 
       expect(comp.state.formData).eql(undefined);
     });
@@ -368,7 +370,10 @@ describe("StringField", () => {
     it("should handle an empty string change event with custom ui:defaultValue", () => {
       const { comp, node } = createFormComponent({
         schema: { type: "string" },
-        uiSchema: { "ui:widget": "textarea", "ui:emptyValue": "default" },
+        uiSchema: {
+          "ui:widget": "textarea",
+          "ui:emptyValue": "default",
+        },
         formData: "x",
       });
 
@@ -1535,7 +1540,9 @@ describe("StringField", () => {
       });
 
       Simulate.change(node.querySelector("[type=file]"), {
-        target: { files: [{ name: "file1.txt", size: 1, type: "type" }] },
+        target: {
+          files: [{ name: "file1.txt", size: 1, type: "type" }],
+        },
       });
 
       return new Promise(setImmediate).then(() =>
diff --git a/test/uiSchema_test.js b/test/uiSchema_test.js
index 68d8123eb..01848d32c 100644
--- a/test/uiSchema_test.js
+++ b/test/uiSchema_test.js
@@ -200,7 +200,11 @@ describe("uiSchema", () => {
       });
 
       it("should render merged ui:widget options for widget referenced as function", () => {
-        const { node } = createFormComponent({ schema, uiSchema, widgets });
+        const { node } = createFormComponent({
+          schema,
+          uiSchema,
+          widgets,
+        });
         const widget = node.querySelector("#funcAll");
 
         expect(widget.style.background).to.equal("purple");
@@ -210,7 +214,11 @@ describe("uiSchema", () => {
       });
 
       it("should render ui:widget default options for widget referenced as function", () => {
-        const { node } = createFormComponent({ schema, uiSchema, widgets });
+        const { node } = createFormComponent({
+          schema,
+          uiSchema,
+          widgets,
+        });
         const widget = node.querySelector("#funcNone");
 
         expect(widget.style.background).to.equal("yellow");
@@ -220,7 +228,11 @@ describe("uiSchema", () => {
       });
 
       it("should render merged ui:widget options for widget referenced as string", () => {
-        const { node } = createFormComponent({ schema, uiSchema, widgets });
+        const { node } = createFormComponent({
+          schema,
+          uiSchema,
+          widgets,
+        });
         const widget = node.querySelector("#stringAll");
 
         expect(widget.style.background).to.equal("blue");
@@ -230,7 +242,11 @@ describe("uiSchema", () => {
       });
 
       it("should render ui:widget default options for widget referenced as string", () => {
-        const { node } = createFormComponent({ schema, uiSchema, widgets });
+        const { node } = createFormComponent({
+          schema,
+          uiSchema,
+          widgets,
+        });
         const widget = node.querySelector("#stringNone");
 
         expect(widget.style.background).to.equal("yellow");
@@ -240,7 +256,11 @@ describe("uiSchema", () => {
       });
 
       it("should ui:option inputType for html5 input types", () => {
-        const { node } = createFormComponent({ schema, uiSchema, widgets });
+        const { node } = createFormComponent({
+          schema,
+          uiSchema,
+          widgets,
+        });
         const widget = node.querySelector("input[type='tel']");
         expect(widget).to.not.be.null;
       });
@@ -280,7 +300,11 @@ describe("uiSchema", () => {
       };
 
       it("should render a nested custom widget", () => {
-        const { node } = createFormComponent({ schema, uiSchema, widgets });
+        const { node } = createFormComponent({
+          schema,
+          uiSchema,
+          widgets,
+        });
 
         expect(node.querySelectorAll(".custom")).to.have.length.of(1);
       });
@@ -335,7 +359,11 @@ describe("uiSchema", () => {
         };
 
         it("should render a custom widget with options", () => {
-          const { node } = createFormComponent({ schema, uiSchema, widgets });
+          const { node } = createFormComponent({
+            schema,
+            uiSchema,
+            widgets,
+          });
 
           expect(node.querySelectorAll(".custom")).to.have.length.of(1);
         });
@@ -1766,7 +1794,11 @@ describe("uiSchema", () => {
           };
           const formData = ["a", "b"];
 
-          let rendered = createFormComponent({ schema, uiSchema, formData });
+          let rendered = createFormComponent({
+            schema,
+            uiSchema,
+            formData,
+          });
           node = rendered.node;
         });
 
@@ -2050,7 +2082,11 @@ describe("uiSchema", () => {
           };
           const formData = ["a", "b"];
 
-          let rendered = createFormComponent({ schema, uiSchema, formData });
+          let rendered = createFormComponent({
+            schema,
+            uiSchema,
+            formData,
+          });
           node = rendered.node;
         });
 
diff --git a/test/utils_test.js b/test/utils_test.js
index 1e3b0141e..2ada07514 100644
--- a/test/utils_test.js
+++ b/test/utils_test.js
@@ -145,7 +145,14 @@ describe("utils", () => {
           properties: {
             level1: {
               type: "object",
-              default: { level2: { leaf1: 1, leaf2: 1, leaf3: 1, leaf4: 1 } },
+              default: {
+                level2: {
+                  leaf1: 1,
+                  leaf2: 1,
+                  leaf3: 1,
+                  leaf4: 1,
+                },
+              },
               properties: {
                 level2: {
                   type: "object",
@@ -166,9 +173,13 @@ describe("utils", () => {
           },
         };
         expect(
-          getDefaultFormState(schema, { level1: { level2: { leaf4: 4 } } })
+          getDefaultFormState(schema, {
+            level1: { level2: { leaf4: 4 } },
+          })
         ).eql({
-          level1: { level2: { leaf1: 1, leaf2: 2, leaf3: 3, leaf4: 4 } },
+          level1: {
+            level2: { leaf1: 1, leaf2: 2, leaf3: 3, leaf4: 4 },
+          },
         });
       });
 
@@ -183,7 +194,9 @@ describe("utils", () => {
             },
           },
         };
-        expect(getDefaultFormState(schema, {})).eql({ level1: [1, 2, 3] });
+        expect(getDefaultFormState(schema, {})).eql({
+          level1: [1, 2, 3],
+        });
       });
 
       it("should use parent defaults for ArrayFields if declared in parent", () => {
@@ -197,7 +210,9 @@ describe("utils", () => {
             },
           },
         };
-        expect(getDefaultFormState(schema, {})).eql({ level1: [1, 2, 3] });
+        expect(getDefaultFormState(schema, {})).eql({
+          level1: [1, 2, 3],
+        });
       });
 
       it("should map item defaults to fixed array default", () => {
@@ -318,7 +333,10 @@ describe("utils", () => {
     describe("uniqueItems is true", () => {
       describe("schema items enum is an array", () => {
         it("should be true", () => {
-          let schema = { items: { enum: ["foo", "bar"] }, uniqueItems: true };
+          let schema = {
+            items: { enum: ["foo", "bar"] },
+            uniqueItems: true,
+          };
           expect(isMultiSelect(schema)).to.be.true;
         });
       });
@@ -359,13 +377,18 @@ describe("utils", () => {
           items: { $ref: "#/definitions/FooItem" },
           uniqueItems: true,
         };
-        const definitions = { FooItem: { type: "string", enum: ["foo"] } };
+        const definitions = {
+          FooItem: { type: "string", enum: ["foo"] },
+        };
         expect(isMultiSelect(schema, definitions)).to.be.true;
       });
     });
 
     it("should be false if uniqueItems is false", () => {
-      const schema = { items: { enum: ["foo", "bar"] }, uniqueItems: false };
+      const schema = {
+        items: { enum: ["foo", "bar"] },
+        uniqueItems: false,
+      };
       expect(isMultiSelect(schema)).to.be.false;
     });
   });
@@ -450,7 +473,9 @@ describe("utils", () => {
         const obj1 = { a: { b: [1] } };
         const obj2 = { a: { b: [2] } };
 
-        expect(mergeObjects(obj1, obj2, true)).eql({ a: { b: [1, 2] } });
+        expect(mergeObjects(obj1, obj2, true)).eql({
+          a: { b: [1, 2] },
+        });
       });
     });
   });
diff --git a/test/validate_test.js b/test/validate_test.js
index c5c274833..d7eb36191 100644
--- a/test/validate_test.js
+++ b/test/validate_test.js
@@ -134,6 +134,42 @@ describe("Validation", () => {
         expect(errors[0].message).to.equal(newErrorMessage);
       });
     });
+
+    describe("Invalid schema", () => {
+      const schema = {
+        type: "object",
+        properties: {
+          foo: {
+            type: "string",
+            required: "invalid_type_non_array",
+          },
+        },
+      };
+
+      let errors, errorSchema;
+
+      beforeEach(() => {
+        const result = validateFormData({ foo: 42 }, schema);
+        errors = result.errors;
+        errorSchema = result.errorSchema;
+      });
+
+      it("should return an error list", () => {
+        expect(errors).to.have.length.of(1);
+        expect(errors[0].name).eql("type");
+        expect(errors[0].property).eql(".properties['foo'].required");
+        expect(errors[0].message).eql("should be array");
+      });
+
+      it("should return an errorSchema", () => {
+        expect(errorSchema.properties.foo.required.__errors).to.have.length.of(
+          1
+        );
+        expect(errorSchema.properties.foo.required.__errors[0]).eql(
+          "should be array"
+        );
+      });
+    });
   });
 
   describe("Form integration", () => {