Repository: mozilla-services/react-jsonschema-form
Pull request #807 (2018-01-11 16:21:54)

Make .editorconfig valid
### Reasons for making this change

The .editorconfig is not a valid editorconfig.

This fixes for example the EditorConfigVim plugin, 

When you look at http://editorconfig.org/#file-format-details, in the example, the way to have some rule applied to all files is by using the `[*]` filename

### 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/.editorconfig b/.editorconfig
index 2c01c1402..38fb58c89 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,3 +1,5 @@
+[*]
+
 indent_style = space
 indent_size = 2
 charset = utf-8
diff --git a/README.md b/README.md
index 57d4b883e..9326e64c1 100644
--- a/README.md
+++ b/README.md
@@ -997,6 +997,19 @@ The following props are passed to `ErrorList`
 - `uiSchema`: The uiSchema that was passed to `Form`.
 - `formContext`: The `formContext` object that you passed to Form.
 
+### Id prefix
+
+To avoid collisions with existing ids in the DOM, it is possible to change the prefix used for ids (the default is `root`).
+
+```jsx
+render((
+  <Form schema={schema}
+        idPrefix={"rjsf_prefix"}/>,
+), document.getElementById("app"));
+```
+
+This will render `<input id="rjsf_prefix_key">` instead of `<input id="root_key">`
+
 ### Custom widgets and fields
 
 The API allows to specify your own custom *widget* and *field* components:
diff --git a/src/components/Form.js b/src/components/Form.js
index 163fc33fe..603ae6a35 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -52,7 +52,8 @@ export default class Form extends Component {
       retrievedSchema,
       uiSchema["ui:rootFieldId"],
       definitions,
-      formData
+      formData,
+      props.idPrefix
     );
     return {
       schema,
diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js
index 67f433394..a71bbaa51 100644
--- a/src/components/fields/ArrayField.js
+++ b/src/components/fields/ArrayField.js
@@ -330,6 +330,7 @@ class ArrayField extends Component {
       formContext,
       onBlur,
       onFocus,
+      idPrefix,
     } = this.props;
     const title = schema.title === undefined ? name : schema.title;
     const { ArrayFieldTemplate, definitions, fields } = registry;
@@ -345,7 +346,8 @@ class ArrayField extends Component {
           itemSchema,
           itemIdPrefix,
           definitions,
-          item
+          item,
+          idPrefix
         );
         return this.renderArrayFieldItem({
           index,
@@ -464,6 +466,7 @@ class ArrayField extends Component {
       uiSchema,
       formData,
       errorSchema,
+      idPrefix,
       idSchema,
       name,
       required,
@@ -508,7 +511,8 @@ class ArrayField extends Component {
           itemSchema,
           itemIdPrefix,
           definitions,
-          item
+          item,
+          idPrefix
         );
         const itemUiSchema = additional
           ? uiSchema.additionalItems || {}
diff --git a/src/components/widgets/TextWidget.js b/src/components/widgets/TextWidget.js
index 027e4e26f..128eab1cb 100644
--- a/src/components/widgets/TextWidget.js
+++ b/src/components/widgets/TextWidget.js
@@ -9,6 +9,7 @@ function TextWidget(props) {
 if (process.env.NODE_ENV !== "production") {
   TextWidget.propTypes = {
     value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+    id: PropTypes.string,
   };
 }
 
diff --git a/src/utils.js b/src/utils.js
index ec0033284..e741510f1 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -619,16 +619,22 @@ export function shouldRender(comp, nextProps, nextState) {
   return !deepEquals(props, nextProps) || !deepEquals(state, nextState);
 }
 
-export function toIdSchema(schema, id, definitions, formData = {}) {
+export function toIdSchema(
+  schema,
+  id,
+  definitions,
+  formData = {},
+  idPrefix = "root"
+) {
   const idSchema = {
-    $id: id || "root",
+    $id: id || idPrefix,
   };
   if ("$ref" in schema) {
     const _schema = retrieveSchema(schema, definitions, formData);
-    return toIdSchema(_schema, id, definitions, formData);
+    return toIdSchema(_schema, id, definitions, formData, idPrefix);
   }
   if ("items" in schema && !schema.items.$ref) {
-    return toIdSchema(schema.items, id, definitions, formData);
+    return toIdSchema(schema.items, id, definitions, formData, idPrefix);
   }
   if (schema.type !== "object") {
     return idSchema;
@@ -636,7 +642,13 @@ export function toIdSchema(schema, id, definitions, formData = {}) {
   for (const name in schema.properties || {}) {
     const field = schema.properties[name];
     const fieldId = idSchema.$id + "_" + name;
-    idSchema[name] = toIdSchema(field, fieldId, definitions, formData[name]);
+    idSchema[name] = toIdSchema(
+      field,
+      fieldId,
+      definitions,
+      formData[name],
+      idPrefix
+    );
   }
   return idSchema;
 }


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index d1cbfdb51..ec2658394 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -44,6 +44,30 @@ describe("Form", () => {
     });
   });
 
+  describe("Option idPrefix", function() {
+    it("should change the rendered ids", 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"]);
+    });
+  });
+
   describe("Custom field template", () => {
     const schema = {
       type: "object",
diff --git a/test/StringField_test.js b/test/StringField_test.js
index ef65f72da..af02df60b 100644
--- a/test/StringField_test.js
+++ b/test/StringField_test.js
@@ -237,7 +237,6 @@ describe("StringField", () => {
         },
       });
 
-      console.log(node.querySelectorAll(".field option")[0].innerHTML);
       expect(node.querySelectorAll(".field option")[0].textContent).eql("Test");
     });
 
diff --git a/test/utils_test.js b/test/utils_test.js
index 09b9d8dea..1e3b0141e 100644
--- a/test/utils_test.js
+++ b/test/utils_test.js
@@ -1028,6 +1028,29 @@ describe("utils", () => {
         bar: { $id: "root_bar" },
       });
     });
+
+    it("should handle idPrefix parameter", () => {
+      const schema = {
+        definitions: {
+          testdef: {
+            type: "object",
+            properties: {
+              foo: { type: "string" },
+              bar: { type: "string" },
+            },
+          },
+        },
+        $ref: "#/definitions/testdef",
+      };
+
+      expect(toIdSchema(schema, undefined, schema.definitions, {}, "rjsf")).eql(
+        {
+          $id: "rjsf",
+          foo: { $id: "rjsf_foo" },
+          bar: { $id: "rjsf_bar" },
+        }
+      );
+    });
   });
 
   describe("parseDateString()", () => {