Repository: mozilla-services/react-jsonschema-form
Pull request #778 (2017-11-28 09:09:53)

Generating idSchema based on dependency changes
### Reasons for making this change

[Library throwing an error regarding `id` prop not being passed. This should fix the issue]

If this is related to existing tickets, include links to them as well.

### 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
* [X] **I'm adding or updating code**
  - [X] I've added and/or updated tests
  - [ ] I've updated docs if needed
  - [X] 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


edi9999: I think this would fix #803. Can we merge this ?

Patch:
diff --git a/README.md b/README.md
index ecf859c25..57d4b883e 100644
--- a/README.md
+++ b/README.md
@@ -201,7 +201,7 @@ WARNING: If you have situations where your parent component can re-render, make
 You can pass a function as the `onSubmit` prop of your `Form` component to listen to when the form is submitted and its data are valid. It will be passed a result object having a `formData` attribute, which is the valid form data you're usually after:
 
 ```js
-const onSubmit = ({form}) => console.log("Data submitted: "+form.formData);
+const onSubmit = ({formData}) => console.log("Data submitted: ",  formData);
 
 render((
   <Form schema={schema}
@@ -890,7 +890,7 @@ The following props are passed to each `ArrayFieldTemplate`:
 - `disabled`: A boolean value stating if the array is disabled.
 - `idSchema`: Object
 - `items`: An array of objects representing the items in the array. Each of the items represent a child with properties described below.
-- `onAddClick: (event) => (event) => void`: Returns a function that adds a new item to the array.
+- `onAddClick: (event) => void`: A function that adds a new item to the array.
 - `readonly`: A boolean value stating if the array is read-only.
 - `required`: A boolean value stating if the array is required.
 - `schema`: The schema object for this array.
@@ -1228,7 +1228,6 @@ i.glyphicon { display: none; }
 .array-item-move-up::after { content: 'Move Up'; }
 .array-item-move-down::after { content: 'Move Down'; }
 .array-item-remove::after { content: 'Remove'; }
-}
 ```
 
 ### Custom SchemaField
diff --git a/src/components/Form.js b/src/components/Form.js
index 79215ffae..163fc33fe 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -4,6 +4,7 @@ import PropTypes from "prop-types";
 import { default as DefaultErrorList } from "./ErrorList";
 import {
   getDefaultFormState,
+  retrieveSchema,
   shouldRender,
   toIdSchema,
   setState,
@@ -39,6 +40,8 @@ export default class Form extends Component {
     const mustValidate = edit && !props.noValidate && liveValidate;
     const { definitions } = schema;
     const formData = getDefaultFormState(schema, props.formData, definitions);
+    const retrievedSchema = retrieveSchema(schema, definitions, formData);
+
     const { errors, errorSchema } = mustValidate
       ? this.validate(formData, schema)
       : {
@@ -46,7 +49,7 @@ export default class Form extends Component {
           errorSchema: state.errorSchema || {},
         };
     const idSchema = toIdSchema(
-      schema,
+      retrievedSchema,
       uiSchema["ui:rootFieldId"],
       definitions,
       formData
diff --git a/src/components/fields/SchemaField.js b/src/components/fields/SchemaField.js
index 4c9b109cb..0b3a0aeac 100644
--- a/src/components/fields/SchemaField.js
+++ b/src/components/fields/SchemaField.js
@@ -8,6 +8,7 @@ import {
   getUiOptions,
   isFilesArray,
   deepEquals,
+  getSchemaType,
 } from "../../utils";
 import UnsupportedField from "./UnsupportedField";
 
@@ -29,7 +30,8 @@ function getFieldComponent(schema, uiSchema, idSchema, fields) {
   if (typeof field === "string" && field in fields) {
     return fields[field];
   }
-  const componentName = COMPONENT_TYPES[schema.type];
+
+  const componentName = COMPONENT_TYPES[getSchemaType(schema)];
   return componentName in fields
     ? fields[componentName]
     : () => {
diff --git a/src/utils.js b/src/utils.js
index d57ab7761..ec0033284 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -62,8 +62,16 @@ export function getDefaultRegistry() {
   };
 }
 
+export function getSchemaType(schema) {
+  let { type } = schema;
+  if (!type && schema.enum) {
+    type = "string";
+  }
+  return type;
+}
+
 export function getWidget(schema, widget, registeredWidgets = {}) {
-  const { type } = schema;
+  const type = getSchemaType(schema);
 
   function mergeOptions(Widget) {
     // cache return value as property of widget for proper react reconciliation


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index 282134448..d1cbfdb51 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -1259,6 +1259,102 @@ describe("Form", () => {
     });
   });
 
+  describe("idSchema updates based on formData", () => {
+    const schema = {
+      type: "object",
+      properties: {
+        a: { type: "string", enum: ["int", "bool"] },
+      },
+      dependencies: {
+        a: {
+          oneOf: [
+            {
+              properties: {
+                a: { enum: ["int"] },
+              },
+            },
+            {
+              properties: {
+                a: { enum: ["bool"] },
+                b: { type: "boolean" },
+              },
+            },
+          ],
+        },
+      },
+    };
+
+    it("should not update idSchema for a falsey value", () => {
+      const formData = { a: "int" };
+      const { comp } = createFormComponent({ schema, formData });
+      comp.componentWillReceiveProps({
+        schema: {
+          type: "object",
+          properties: {
+            a: { type: "string", enum: ["int", "bool"] },
+          },
+          dependencies: {
+            a: {
+              oneOf: [
+                {
+                  properties: {
+                    a: { enum: ["int"] },
+                  },
+                },
+                {
+                  properties: {
+                    a: { enum: ["bool"] },
+                    b: { type: "boolean" },
+                  },
+                },
+              ],
+            },
+          },
+        },
+        formData: { a: "int" },
+      });
+      expect(comp.state.idSchema).eql({ $id: "root", a: { $id: "root_a" } });
+    });
+
+    it("should update idSchema based on truthy value", () => {
+      const formData = {
+        a: "int",
+      };
+      const { comp } = createFormComponent({ schema, formData });
+      comp.componentWillReceiveProps({
+        schema: {
+          type: "object",
+          properties: {
+            a: { type: "string", enum: ["int", "bool"] },
+          },
+          dependencies: {
+            a: {
+              oneOf: [
+                {
+                  properties: {
+                    a: { enum: ["int"] },
+                  },
+                },
+                {
+                  properties: {
+                    a: { enum: ["bool"] },
+                    b: { type: "boolean" },
+                  },
+                },
+              ],
+            },
+          },
+        },
+        formData: { a: "bool" },
+      });
+      expect(comp.state.idSchema).eql({
+        $id: "root",
+        a: { $id: "root_a" },
+        b: { $id: "root_b" },
+      });
+    });
+  });
+
   describe("Attributes", () => {
     const formProps = {
       schema: {},
diff --git a/test/StringField_test.js b/test/StringField_test.js
index e25af5b4b..ef65f72da 100644
--- a/test/StringField_test.js
+++ b/test/StringField_test.js
@@ -191,6 +191,16 @@ describe("StringField", () => {
       expect(node.querySelectorAll(".field select")).to.have.length.of(1);
     });
 
+    it("should render a string field for an enum without a type", () => {
+      const { node } = createFormComponent({
+        schema: {
+          enum: ["foo", "bar"],
+        },
+      });
+
+      expect(node.querySelectorAll(".field select")).to.have.length.of(1);
+    });
+
     it("should render a string field with a label", () => {
       const { node } = createFormComponent({
         schema: {