Repository: mozilla-services/react-jsonschema-form (js)

Issue:
Recursive references are not supported
### Prerequisites

- [x] I have read the [documentation](https://github.com/mozilla-services/react-jsonschema-form/blob/master/README.md#readme);
- [x] In the case of a bug report, I understand that providing a [SSCCE](http://sscce.org/) example is tremendously useful to the maintainers.

### Description

Recursive references are not supported.

### Steps to Reproduce

1. Define a schema that utilises recursive referencing:
    ```json
    {
      "definitions": {
        "bar": { "$ref": "#/definitions/qux" },
        "qux": { "type": "string" }
      },
      "type": "object",
      "properties": {
        "foo": {
          "$ref": "#/definitions/bar"
        }
      }
    }
    ```

    or

    ```json
    {
      "definitions": {
        "testdef": { "$ref": "#/definitions/bar" },
        "bar": {
          "type": "object",
          "properties": {
            "qux": { "type": "string" }
          }
        }
      },
      "type": "object",
      "properties": {
        "foo": { "$ref": "#/definitions/testdef/properties/qux" }
      }
    }
    ```
2. Attempt to create a form using those schema.

#### Expected behavior

Form to be rendered.

#### Actual behavior

In the first case above ([shared playground link](https://mozilla-services.github.io/react-jsonschema-form/#eyJmb3JtRGF0YSI6e30sInNjaGVtYSI6eyJkZWZpbml0aW9ucyI6eyJiYXIiOnsiJHJlZiI6IiMvZGVmaW5pdGlvbnMvcXV4In0sInF1eCI6eyJ0eXBlIjoic3RyaW5nIn19LCJ0eXBlIjoib2JqZWN0IiwicHJvcGVydGllcyI6eyJmb28iOnsiJHJlZiI6IiMvZGVmaW5pdGlvbnMvYmFyIn19fSwidWlTY2hlbWEiOnt9fQ==)), a form is rendered—but where the field is expected, the following message appears:

> **Unsupported field schema { "$ref": "#/definitions/qux" }**

In the second case above ([shared playground link](https://mozilla-services.github.io/react-jsonschema-form/#eyJmb3JtRGF0YSI6e30sInNjaGVtYSI6eyJkZWZpbml0aW9ucyI6eyJ0ZXN0ZGVmIjp7IiRyZWYiOiIjL2RlZmluaXRpb25zL2JhciJ9LCJiYXIiOnsidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsicXV4Ijp7InR5cGUiOiJzdHJpbmcifX19fSwidHlwZSI6Im9iamVjdCIsInByb3BlcnRpZXMiOnsiZm9vIjp7IiRyZWYiOiIjL2RlZmluaXRpb25zL3Rlc3RkZWYvcHJvcGVydGllcy9xdXgifX19LCJ1aVNjaGVtYSI6e319)), no form is rendered.  Console shows:

> **Error: Could not find a definition for #/definitions/testdef/properties/qux.**

### Version

0.46.0

Pull request:
#556 - handle recursive references to deep schema definitions
### Reasons for making this change

Fixes #556 (case 2)

Also follows a series of recursive references, for example,
```
{
        definitions: {
          testdef: {
            $ref: "#/definitions/testdefref1",
          },
          testdefref1: {
            $ref: "#/definitions/testdefref2",
          },
          testdefref2: {
            type: "object",
            properties: {
              bar: { type: "string" },
            },
          },
        },
        type: "object",
        properties: {
          foo: { $ref: "#/definitions/testdef/properties/bar" },
        },
}
```

### 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


Patch:
diff --git a/src/utils.js b/src/utils.js
index 929294274..8c69cccae 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -394,6 +394,9 @@ function findSchemaDefinition($ref, definitions = {}) {
     let current = definitions;
     for (let part of parts) {
       part = part.replace(/~1/g, "/").replace(/~0/g, "~");
+      while (current.hasOwnProperty("$ref")) {
+        current = findSchemaDefinition(current.$ref, definitions);
+      }
       if (current.hasOwnProperty(part)) {
         current = current[part];
       } else {


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index 793e3a075..23b6ea816 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -541,6 +541,92 @@ describe("Form", () => {
       expect(node.querySelector("#root_children_0_name")).to.exist;
     });
 
+    it("should follow recursive references", () => {
+      const schema = {
+        definitions: {
+          bar: { $ref: "#/definitions/qux" },
+          qux: { type: "string" },
+        },
+        type: "object",
+        required: ["foo"],
+        properties: {
+          foo: { $ref: "#/definitions/bar" },
+        },
+      };
+      const { node } = createFormComponent({ schema });
+
+      expect(node.querySelectorAll("input[type=text]")).to.have.length.of(1);
+    });
+
+    it("should follow multiple recursive references", () => {
+      const schema = {
+        definitions: {
+          bar: { $ref: "#/definitions/bar2" },
+          bar2: { $ref: "#/definitions/qux" },
+          qux: { type: "string" },
+        },
+        type: "object",
+        required: ["foo"],
+        properties: {
+          foo: { $ref: "#/definitions/bar" },
+        },
+      };
+      const { node } = createFormComponent({ schema });
+
+      expect(node.querySelectorAll("input[type=text]")).to.have.length.of(1);
+    });
+
+    it("should handle recursive references to deep schema definitions", () => {
+      const schema = {
+        definitions: {
+          testdef: {
+            $ref: "#/definitions/testdefref",
+          },
+          testdefref: {
+            type: "object",
+            properties: {
+              bar: { type: "string" },
+            },
+          },
+        },
+        type: "object",
+        properties: {
+          foo: { $ref: "#/definitions/testdef/properties/bar" },
+        },
+      };
+
+      const { node } = createFormComponent({ schema });
+
+      expect(node.querySelectorAll("input[type=text]")).to.have.length.of(1);
+    });
+
+    it("should handle multiple recursive references to deep schema definitions", () => {
+      const schema = {
+        definitions: {
+          testdef: {
+            $ref: "#/definitions/testdefref1",
+          },
+          testdefref1: {
+            $ref: "#/definitions/testdefref2",
+          },
+          testdefref2: {
+            type: "object",
+            properties: {
+              bar: { type: "string" },
+            },
+          },
+        },
+        type: "object",
+        properties: {
+          foo: { $ref: "#/definitions/testdef/properties/bar" },
+        },
+      };
+
+      const { node } = createFormComponent({ schema });
+
+      expect(node.querySelectorAll("input[type=text]")).to.have.length.of(1);
+    });
+
     it("should priorize definition over schema type property", () => {
       // Refs bug #140
       const schema = {