Repository: mozilla-services/react-jsonschema-form
Pull request #1223 (2019-03-16 22:39:54)

Document placeholder and autofocus widget props
### Reasons for making this change

Documenting placeholder and autofocus widget props.

If this is related to existing tickets, include links to them as well.

### Checklist

* [x] **I'm updating documentation**
  - [x] I've [checked the rendering](https://react-jsonschema-form.readthedocs.io/en/latest/#contributing) of the Markdown text I've added


Patch:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..498baa3fb
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,15 @@
+# Community Participation Guidelines
+
+This repository is governed by Mozilla's code of conduct and etiquette guidelines. 
+For more details, please read the
+[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 
+
+## How to Report
+For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
+
+<!--
+## Project Specific Etiquette
+
+In some cases, there will be additional project etiquette i.e.: (https://bugzilla.mozilla.org/page.cgi?id=etiquette.html).
+Please update for your project.
+-->
diff --git a/docs/advanced-customization.md b/docs/advanced-customization.md
index e74fb9eaa..69ec69fef 100644
--- a/docs/advanced-customization.md
+++ b/docs/advanced-customization.md
@@ -257,9 +257,11 @@ The following props are passed to custom widget components:
 - `id`: The generated id for this field;
 - `schema`: The JSONSchema subschema object for this field;
 - `value`: The current value for this field;
+- `placeholder`: the placeholder for the field, if any;
 - `required`: The required status of this field;
 - `disabled`: `true` if the widget is disabled;
 - `readonly`: `true` if the widget is read-only;
+- `autofocus`: `true` if the widget should autofocus;
 - `onChange`: The value change event handler; call it with the new value everytime it changes;
 - `onBlur`: The input blur event handler; call it with the the widget id and value;
 - `onFocus`: The input focus event handler; call it with the the widget id and value;
diff --git a/docs/form-customization.md b/docs/form-customization.md
index 07f77d938..a42c0f6fa 100644
--- a/docs/form-customization.md
+++ b/docs/form-customization.md
@@ -694,8 +694,7 @@ The `Form` component supports the following html attributes:
   action="/users/list"
   autocomplete="off"
   enctype="multipart/form-data"
-  acceptcharset="ISO-8859-1"
-  schema={} />
+  acceptcharset="ISO-8859-1" />
 ```
 
 ### Disabling a form
diff --git a/src/utils.js b/src/utils.js
index 3445ad810..ce13634b5 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -229,6 +229,9 @@ export function getUiOptions(uiSchema) {
 }
 
 export function isObject(thing) {
+  if (thing instanceof File) {
+    return false;
+  }
   return typeof thing === "object" && thing !== null && !Array.isArray(thing);
 }
 
@@ -290,28 +293,32 @@ export function orderProperties(properties, order) {
       ? `properties '${arr.join("', '")}'`
       : `property '${arr[0]}'`;
   const propertyHash = arrayToHash(properties);
-  const orderHash = arrayToHash(order);
   const extraneous = order.filter(prop => prop !== "*" && !propertyHash[prop]);
   if (extraneous.length) {
-    throw new Error(
+    console.warn(
       `uiSchema order list contains extraneous ${errorPropList(extraneous)}`
     );
   }
+  const orderFiltered = order.filter(
+    prop => prop === "*" || propertyHash[prop]
+  );
+  const orderHash = arrayToHash(orderFiltered);
+
   const rest = properties.filter(prop => !orderHash[prop]);
-  const restIndex = order.indexOf("*");
+  const restIndex = orderFiltered.indexOf("*");
   if (restIndex === -1) {
     if (rest.length) {
       throw new Error(
         `uiSchema order list does not contain ${errorPropList(rest)}`
       );
     }
-    return order;
+    return orderFiltered;
   }
-  if (restIndex !== order.lastIndexOf("*")) {
+  if (restIndex !== orderFiltered.lastIndexOf("*")) {
     throw new Error("uiSchema order list contains more than one wildcard item");
   }
 
-  const complete = [...order];
+  const complete = [...orderFiltered];
   complete.splice(restIndex, 1, ...rest);
   return complete;
 }


Test patch:
diff --git a/test/ObjectField_test.js b/test/ObjectField_test.js
index e8c04af47..a44dbd639 100644
--- a/test/ObjectField_test.js
+++ b/test/ObjectField_test.js
@@ -231,7 +231,7 @@ describe("ObjectField", () => {
       expect(labels).eql(["baz", "bar", "qux", "foo"]);
     });
 
-    it("should throw when order list contains an extraneous property", () => {
+    it("should use provided order also if order list contains extraneous properties", () => {
       const { node } = createFormComponent({
         schema,
         uiSchema: {
@@ -239,9 +239,12 @@ describe("ObjectField", () => {
         },
       });
 
-      expect(node.querySelector(".config-error").textContent).to.match(
-        /contains extraneous properties 'wut\?', 'huh\?'/
+      const labels = [].map.call(
+        node.querySelectorAll(".field > label"),
+        l => l.textContent
       );
+
+      expect(labels).eql(["baz", "qux", "bar", "foo"]);
     });
 
     it("should throw when order list misses an existing property", () => {
diff --git a/test/setup-jsdom.js b/test/setup-jsdom.js
index 3cbf2cd12..d9f1115da 100644
--- a/test/setup-jsdom.js
+++ b/test/setup-jsdom.js
@@ -6,6 +6,7 @@ if (!global.hasOwnProperty("window")) {
   global.document = jsdom.jsdom("<!doctype html><html><body></body></html>");
   global.window = document.defaultView;
   global.navigator = global.window.navigator;
+  global.File = global.window.File;
 }
 
 // atob
diff --git a/test/utils_test.js b/test/utils_test.js
index 49f2fb632..2f1ecc369 100644
--- a/test/utils_test.js
+++ b/test/utils_test.js
@@ -2,6 +2,7 @@ import { expect } from "chai";
 
 import {
   asNumber,
+  orderProperties,
   dataURItoBlob,
   deepEquals,
   getDefaultFormState,
@@ -343,6 +344,37 @@ describe("utils", () => {
     });
   });
 
+  describe("orderProperties()", () => {
+    it("should remove from order elements that are not in properties", () => {
+      const properties = ["foo", "baz"];
+      const order = ["foo", "bar", "baz", "qux"];
+      expect(orderProperties(properties, order)).eql(["foo", "baz"]);
+    });
+
+    it("should order properties according to the order", () => {
+      const properties = ["bar", "foo"];
+      const order = ["foo", "bar"];
+      expect(orderProperties(properties, order)).eql(["foo", "bar"]);
+    });
+
+    it("should replace * with properties that are absent in order", () => {
+      const properties = ["foo", "bar", "baz"];
+      const order = ["*", "foo"];
+      expect(orderProperties(properties, order)).eql(["bar", "baz", "foo"]);
+    });
+
+    it("should handle more complex ordering case correctly", () => {
+      const properties = ["foo", "baz", "qux", "bar"];
+      const order = ["quux", "foo", "*", "corge", "baz"];
+      expect(orderProperties(properties, order)).eql([
+        "foo",
+        "qux",
+        "bar",
+        "baz",
+      ]);
+    });
+  });
+
   describe("isConstant", () => {
     it("should return false when neither enum nor const is defined", () => {
       const schema = {};
@@ -517,6 +549,17 @@ describe("utils", () => {
       expect(mergeObjects(obj1, obj2)).eql(expected);
     });
 
+    it("should recursively merge File objects", () => {
+      const file = new File(["test"], "test.txt");
+      const obj1 = {
+        a: {},
+      };
+      const obj2 = {
+        a: file,
+      };
+      expect(mergeObjects(obj1, obj2).a).instanceOf(File);
+    });
+
     describe("concatArrays option", () => {
       it("should not concat arrays by default", () => {
         const obj1 = { a: [1] };