Repository: mozilla-services/react-jsonschema-form
Pull request #1072 (2018-10-30 14:03:06)

Add licence to repository
### Reasons for making this change

Licence was only described in README and was not explicitly added to the repository. 




Patch:
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
index bdf147228..ca4428cf0 100644
--- a/README.md
+++ b/README.md
@@ -370,7 +370,7 @@ Please note that, even though they are standardized, `datetime-local` and `date`
 
 ![](http://i.imgur.com/VF5tY60.png)
 
-You can customize the list of years displayed in the `year` dropdown by providing a ``yearsRange`` property to ``ui:options`` in your uiSchema:
+You can customize the list of years displayed in the `year` dropdown by providing a ``yearsRange`` property to ``ui:options`` in your uiSchema. Its also possible to remove the `Now` and `Clear` buttons with the `hideNowButton` and `hideClearButton` options.
 
 ```jsx
 uiSchema: {
@@ -379,6 +379,8 @@ uiSchema: {
       "ui:widget": "alt-datetime",
       "ui:options": {
         yearsRange: [1980, 2030],
+        hideNowButton: true,
+        hideClearButton: true,
       },
     },
   },
diff --git a/package-lock.json b/package-lock.json
index bacc22c32..38b2ad003 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7415,9 +7415,9 @@
       "optional": true
     },
     "prettier": {
-      "version": "1.13.2",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.13.2.tgz",
-      "integrity": "sha512-D9oFKkJ7g76fRxkRh9MWBh4j2vbNGO4rtEUJbj46zId5wnm0dwHruoyg4Od9Zqh3WNl0jwxnWSlEGAnl+/thWA==",
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.1.tgz",
+      "integrity": "sha512-4rgV2hyc/5Pk0XHH4VjJWHRgVjgRbpMfLQjREAhHBtyW1UvTFkjJEsueGYNYYZd9mn97K+1qv0EBwm11zoaSgA==",
       "dev": true
     },
     "private": {
diff --git a/package.json b/package.json
index 803cd1acb..e2a1f623d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "react-jsonschema-form",
-  "version": "1.0.5",
+  "version": "1.0.6",
   "description": "A simple React component capable of building HTML forms out of a JSON schema.",
   "scripts": {
     "build:readme": "toctoc README.md -w",
@@ -11,6 +11,7 @@
     "cs-format": "prettier --jsx-bracket-same-line --trailing-comma es5 --use-tabs false --semi --tab-width 2 '{playground,src,test}/**/*.js' --write",
     "dist": "npm run build:lib && npm run build:dist",
     "lint": "eslint src test playground",
+    "prepare": "npm run dist",
     "precommit": "lint-staged",
     "publish-to-gh-pages": "npm run build:playground && gh-pages --dist build/",
     "publish-to-npm": "npm run build:readme && npm run dist && npm publish",
@@ -81,7 +82,7 @@
     "lint-staged": "^3.3.1",
     "mini-css-extract-plugin": "^0.4.3",
     "mocha": "^5.2.0",
-    "prettier": "^1.12.0",
+    "prettier": "^1.15.1",
     "react": "^15.5.0",
     "react-addons-test-utils": "^15.3.2",
     "react-codemirror2": "^4.1.0",
diff --git a/src/components/Form.js b/src/components/Form.js
index ab7dabab7..6863d7437 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -38,13 +38,14 @@ export default class Form extends Component {
 
   componentWillReceiveProps(nextProps) {
     const nextState = this.getStateFromProps(nextProps);
-    this.setState(nextState);
     if (
       !deepEquals(nextState.formData, nextProps.formData) &&
+      !deepEquals(nextState.formData, this.state.formData) &&
       this.props.onChange
     ) {
       this.props.onChange(nextState);
     }
+    this.setState(nextState);
   }
 
   getStateFromProps(props) {
diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js
index 4985bf3f1..a85bd6fa0 100644
--- a/src/components/fields/ArrayField.js
+++ b/src/components/fields/ArrayField.js
@@ -555,8 +555,8 @@ class ArrayField extends Component {
         const itemUiSchema = additional
           ? uiSchema.additionalItems || {}
           : Array.isArray(uiSchema.items)
-            ? uiSchema.items[index]
-            : uiSchema.items || {};
+          ? uiSchema.items[index]
+          : uiSchema.items || {};
         const itemErrorSchema = errorSchema ? errorSchema[index] : undefined;
 
         return this.renderArrayFieldItem({
diff --git a/src/components/fields/BooleanField.js b/src/components/fields/BooleanField.js
index 8f10dccff..00014f28c 100644
--- a/src/components/fields/BooleanField.js
+++ b/src/components/fields/BooleanField.js
@@ -21,6 +21,8 @@ function BooleanField(props) {
     readonly,
     autofocus,
     onChange,
+    onFocus,
+    onBlur,
     rawErrors,
   } = props;
   const { title } = schema;
@@ -37,6 +39,8 @@ function BooleanField(props) {
       schema={schema}
       id={idSchema && idSchema.$id}
       onChange={onChange}
+      onFocus={onFocus}
+      onBlur={onBlur}
       label={title === undefined ? name : title}
       value={formData}
       required={required}
@@ -56,6 +60,8 @@ if (process.env.NODE_ENV !== "production") {
     uiSchema: PropTypes.object,
     idSchema: PropTypes.object,
     onChange: PropTypes.func.isRequired,
+    onFocus: PropTypes.func,
+    onBlur: PropTypes.func,
     formData: PropTypes.bool,
     required: PropTypes.bool,
     disabled: PropTypes.bool,
diff --git a/src/components/fields/TitleField.js b/src/components/fields/TitleField.js
index 30ed634df..f33f98384 100644
--- a/src/components/fields/TitleField.js
+++ b/src/components/fields/TitleField.js
@@ -5,8 +5,12 @@ const REQUIRED_FIELD_SYMBOL = "*";
 
 function TitleField(props) {
   const { id, title, required } = props;
-  const legend = required ? title + REQUIRED_FIELD_SYMBOL : title;
-  return <legend id={id}>{legend}</legend>;
+  return (
+    <legend id={id}>
+      {title}
+      {required && <span className="required">{REQUIRED_FIELD_SYMBOL}</span>}
+    </legend>
+  );
 }
 
 if (process.env.NODE_ENV !== "production") {
diff --git a/src/components/fields/UnsupportedField.js b/src/components/fields/UnsupportedField.js
index 33f875256..56f3c0a9c 100644
--- a/src/components/fields/UnsupportedField.js
+++ b/src/components/fields/UnsupportedField.js
@@ -6,12 +6,11 @@ function UnsupportedField({ schema, idSchema, reason }) {
     <div className="unsupported-field">
       <p>
         Unsupported field schema
-        {idSchema &&
-          idSchema.$id && (
-            <span>
-              {" for"} field <code>{idSchema.$id}</code>
-            </span>
-          )}
+        {idSchema && idSchema.$id && (
+          <span>
+            {" for"} field <code>{idSchema.$id}</code>
+          </span>
+        )}
         {reason && <em>: {reason}</em>}.
       </p>
       {schema && <pre>{JSON.stringify(schema, null, 2)}</pre>}
diff --git a/src/components/widgets/AltDateWidget.js b/src/components/widgets/AltDateWidget.js
index 18e8e7ffc..ce8fd275f 100644
--- a/src/components/widgets/AltDateWidget.js
+++ b/src/components/widgets/AltDateWidget.js
@@ -125,7 +125,15 @@ class AltDateWidget extends Component {
   }
 
   render() {
-    const { id, disabled, readonly, autofocus, registry, onBlur } = this.props;
+    const {
+      id,
+      disabled,
+      readonly,
+      autofocus,
+      registry,
+      onBlur,
+      options,
+    } = this.props;
     return (
       <ul className="list-inline">
         {this.dateElementProps.map((elemProps, i) => (
@@ -142,19 +150,27 @@ class AltDateWidget extends Component {
             />
           </li>
         ))}
-        <li>
-          <a href="#" className="btn btn-info btn-now" onClick={this.setNow}>
-            Now
-          </a>
-        </li>
-        <li>
-          <a
-            href="#"
-            className="btn btn-warning btn-clear"
-            onClick={this.clear}>
-            Clear
-          </a>
-        </li>
+        {(options.hideNowButton !== "undefined"
+          ? !options.hideNowButton
+          : true) && (
+          <li>
+            <a href="#" className="btn btn-info btn-now" onClick={this.setNow}>
+              Now
+            </a>
+          </li>
+        )}
+        {(options.hideClearButton !== "undefined"
+          ? !options.hideClearButton
+          : true) && (
+          <li>
+            <a
+              href="#"
+              className="btn btn-warning btn-clear"
+              onClick={this.clear}>
+              Clear
+            </a>
+          </li>
+        )}
       </ul>
     );
   }
diff --git a/src/components/widgets/CheckboxesWidget.js b/src/components/widgets/CheckboxesWidget.js
index 779e46124..4f07097f6 100644
--- a/src/components/widgets/CheckboxesWidget.js
+++ b/src/components/widgets/CheckboxesWidget.js
@@ -15,19 +15,22 @@ function deselectValue(value, selected) {
 
 function CheckboxesWidget(props) {
   const { id, disabled, options, value, autofocus, readonly, onChange } = props;
-  const { enumOptions, inline } = options;
+  const { enumOptions, enumDisabled, inline } = options;
   return (
     <div className="checkboxes" id={id}>
       {enumOptions.map((option, index) => {
         const checked = value.indexOf(option.value) !== -1;
-        const disabledCls = disabled || readonly ? "disabled" : "";
+        const itemDisabled =
+          enumDisabled && enumDisabled.indexOf(option.value) != -1;
+        const disabledCls =
+          disabled || itemDisabled || readonly ? "disabled" : "";
         const checkbox = (
           <span>
             <input
               type="checkbox"
               id={`${id}_${index}`}
               checked={checked}
-              disabled={disabled || readonly}
+              disabled={disabled || itemDisabled || readonly}
               autoFocus={autofocus && index === 0}
               onChange={event => {
                 const all = enumOptions.map(({ value }) => value);
diff --git a/src/utils.js b/src/utils.js
index 25cfe6ad3..f8cc7838e 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -548,11 +548,10 @@ function withDependentSchema(
     throw new Error(`invalid: it is some ${typeof oneOf} instead of an array`);
   }
   // Resolve $refs inside oneOf.
-  const resolvedOneOf = oneOf.map(
-    subschema =>
-      subschema.hasOwnProperty("$ref")
-        ? resolveReference(subschema, definitions, formData)
-        : subschema
+  const resolvedOneOf = oneOf.map(subschema =>
+    subschema.hasOwnProperty("$ref")
+      ? resolveReference(subschema, definitions, formData)
+      : subschema
   );
   return withExactlyOneSubschema(
     schema,


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index 630026a82..85a39d7f8 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -5,7 +5,11 @@ import { renderIntoDocument, Simulate } from "react-addons-test-utils";
 import { findDOMNode } from "react-dom";
 
 import Form from "../src";
-import { createFormComponent, createSandbox } from "./test_utils";
+import {
+  createComponent,
+  createFormComponent,
+  createSandbox,
+} from "./test_utils";
 
 describe("Form", () => {
   let sandbox;
@@ -862,7 +866,8 @@ describe("Form", () => {
         comp.componentWillReceiveProps({
           schema: newSchema,
           formData: "some value",
-        }));
+        })
+      );
 
       it("should not call onChange", () => {
         sinon.assert.notCalled(onChangeProp);
@@ -881,7 +886,8 @@ describe("Form", () => {
         comp.componentWillReceiveProps({
           schema: newSchema,
           formData: "something else",
-        }));
+        })
+      );
 
       it("should not call onChange", () => {
         sinon.assert.notCalled(onChangeProp);
@@ -897,7 +903,8 @@ describe("Form", () => {
       };
 
       beforeEach(() =>
-        comp.componentWillReceiveProps({ schema: newSchema, formData: null }));
+        comp.componentWillReceiveProps({ schema: newSchema, formData: null })
+      );
 
       it("should call onChange", () => {
         sinon.assert.calledOnce(onChangeProp);
@@ -905,6 +912,54 @@ describe("Form", () => {
         expect(comp.state.formData).eql("the new default");
       });
     });
+
+    describe("when the onChange prop sets formData to a falsey value", () => {
+      class TestForm extends React.Component {
+        constructor() {
+          super();
+
+          this.state = {
+            formData: {},
+          };
+        }
+
+        onChange = () => {
+          this.setState({ formData: this.props.falseyValue });
+        };
+
+        render() {
+          const schema = {
+            type: "object",
+            properties: {
+              value: {
+                type: "string",
+              },
+            },
+          };
+          return (
+            <Form
+              onChange={this.onChange}
+              schema={schema}
+              formData={this.state.formData}
+            />
+          );
+        }
+      }
+
+      const falseyValues = [0, false, null, undefined, NaN];
+
+      falseyValues.forEach(falseyValue => {
+        it("Should not crash due to 'Maximum call stack size exceeded...'", () => {
+          // It is expected that this will throw an error due to non-matching propTypes,
+          // so the error message needs to be inspected
+          try {
+            createComponent(TestForm, { falseyValue });
+          } catch (e) {
+            expect(e.message).to.not.equal("Maximum call stack size exceeded");
+          }
+        });
+      });
+    });
   });
 
   describe("External formData updates", () => {
diff --git a/test/TitleField_test.js b/test/TitleField_test.js
index ed689ecdd..1c62e4988 100644
--- a/test/TitleField_test.js
+++ b/test/TitleField_test.js
@@ -65,5 +65,7 @@ describe("TitleField", () => {
     const { node } = createComponent(TitleFieldWrapper, props);
 
     expect(node.textContent).to.equal(props.title + "*");
+
+    expect(node.querySelector("span.required").textContent).to.equal("*");
   });
 });