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

Issue:
References to definitions deeper than immediate children of `#/definitions` 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

References to definitions deeper than immediate children of `#/definitions` are not supported.

### Steps to Reproduce

1. Define a schema that references definitions deeper than immediate children of `#/definitions`:
    ```json
    {
      "definitions": {
        "testdef": {
          "type": "object",
          "properties": {
            "bar": {
              "type": "string"
            }
          }
        }
      },
      "type": "object",
      "properties": {
        "foo": {
          "$ref": "#/definitions/testdef/properties/bar"
        }
      }
    }
    ```
2. Attempt to create a form using that schema ([shared playground link](https://mozilla-services.github.io/react-jsonschema-form/#eyJmb3JtRGF0YSI6e30sInNjaGVtYSI6eyJkZWZpbml0aW9ucyI6eyJ0ZXN0ZGVmIjp7InR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImJhciI6eyJ0eXBlIjoic3RyaW5nIn19fX0sInR5cGUiOiJvYmplY3QiLCJwcm9wZXJ0aWVzIjp7ImZvbyI6eyIkcmVmIjoiIy9kZWZpbml0aW9ucy90ZXN0ZGVmL3Byb3BlcnRpZXMvYmFyIn19fSwidWlTY2hlbWEiOnt9fQ==)).

#### Expected behavior

Form to be rendered.

#### Actual behavior

No form rendered.  Console shows:

> **Error: Could not find a definition for #/definitions/testdef/properties/bar.**

### Version

0.46.0

Pull request description:
Handle references to deep schema definitions
### Reasons for making this change

Fixes #553

### Checklist

* [x] **I'm adding or updating code**
  - [x] I've added and/or updated tests
  - [x] 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

Patch:
diff --git a/src/utils.js b/src/utils.js
index cc67899c6..31c8b4ca3 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -314,10 +314,22 @@ export function optionsList(schema) {
 
 function findSchemaDefinition($ref, definitions = {}) {
   // Extract and use the referenced definition if we have it.
-  const match = /#\/definitions\/(.*)$/.exec($ref);
-  if (match && match[1] && definitions.hasOwnProperty(match[1])) {
-    return definitions[match[1]];
+  const match = /^#\/definitions\/(.*)$/.exec($ref);
+  if (match && match[1]) {
+    const parts = match[1].split("/");
+    let current = definitions;
+    for (let part of parts) {
+      part = part.replace(/~1/g, "/").replace(/~0/g, "~");
+      if (current.hasOwnProperty(part)) {
+        current = current[part];
+      } else {
+        // No matching definition found, that's an error (bogus schema?)
+        throw new Error(`Could not find a definition for ${$ref}.`);
+      }
+    }
+    return current;
   }
+
   // No matching definition found, that's an error (bogus schema?)
   throw new Error(`Could not find a definition for ${$ref}.`);
 }


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index 177a0f1e4..6fcaea1a5 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -229,6 +229,27 @@ describe("Form", () => {
       expect(node.querySelectorAll("input[type=text]")).to.have.length.of(1);
     });
 
+    it("should handle references to deep schema definitions", () => {
+      const schema = {
+        definitions: {
+          testdef: {
+            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 referenced definitions for array items", () => {
       const schema = {
         definitions: {
diff --git a/test/utils_test.js b/test/utils_test.js
index 586f4594a..ad42abd5d 100644
--- a/test/utils_test.js
+++ b/test/utils_test.js
@@ -365,6 +365,14 @@ describe("utils", () => {
       expect(retrieveSchema(schema, definitions)).eql(address);
     });
 
+    it("should 'resolve' escaped JSON Pointers", () => {
+      const schema = { $ref: "#/definitions/a~0complex~1name" };
+      const address = { type: "string" };
+      const definitions = { "a~complex/name": address };
+
+      expect(retrieveSchema(schema, definitions)).eql(address);
+    });
+
     it("should priorize local definitions over foreign ones", () => {
       const schema = {
         $ref: "#/definitions/address",