Repository: mozilla-services/react-jsonschema-form
Pull request #1058 (2018-10-19 15:14:41)

Add sumbit method to enable the programmatic submission
### Reasons for making this change

In some user case, the form should be submitted by the javascript program programmatically, for example, the submit button in the UI design is separated from the form.

### Checklist

* [x] **I'm updating documentation**
  - [x] I've checked the rendering of the Markdown text I've added
  - [x] If I'm adding a new section, I've updated the Table of Content
* [x] **I'm adding or updating code**
  - [ ] 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
* [x] **I'm adding a new feature**
  - [ ] I've updated the playground with an example use of the feature



Patch:
diff --git a/README.md b/README.md
index d1c21e276..4ddbabcac 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
         - [Form data changes](#form-data-changes)
         - [Form field blur events](#form-field-blur-events)
         - [Form field focus events](#form-field-focus-events)
+     - [Submit form programmatically](#submit-form-programmatically)
   - [Form customization](#form-customization)
      - [The uiSchema object](#the-uischema-object)
      - [Alternative widgets](#alternative-widgets)
@@ -59,6 +60,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
      - [Field labels](#field-labels)
      - [HTML5 Input Types](#html5-input-types)
      - [Form attributes](#form-attributes)
+     - [Form disable](#form-disable)
   - [Advanced customization](#advanced-customization)
      - [Field template](#field-template)
      - [Array Field Template](#array-field-template)
@@ -240,6 +242,22 @@ Sometimes you may want to trigger events or modify external state when a field h
 
 Sometimes you may want to trigger events or modify external state when a field has been focused, so you can pass an `onFocus` handler, which will receive the id of the input that is focused and the field value.
 
+### Submit form programmatically
+You can use the reference to get your `Form` component and call the `submit` method to submit the form programmatically without a submit button.
+This method will dispatch the `submit` event of the form, and the function, that is passed to `onSubmit` props, will be called.
+
+```js
+const onSubmit = ({formData}) => console.log("Data submitted: ",  formData);
+let yourForm;
+
+render((
+  <Form schema={schema}
+        onSubmit={onSubmit} ref={(form) => {yourForm = form;}}/>
+), document.getElementById("app"));
+
+yourForm.submit();
+```
+
 ## Form customization
 
 ### The `uiSchema` object
@@ -881,6 +899,18 @@ The `Form` component supports the following html attributes:
   schema={} />
 ```
 
+### Form disable
+
+Its possible to disable the whole form by setting the `disabled` prop. The `disabled` prop is then forwarded down thru each field of the form. 
+
+```jsx
+<Form
+  disabled
+  schema={} />
+```
+
+If you just want to disable some of the fields see the `ui:disabled` parameter in the uiSchema directive. 
+
 ## Advanced customization
 
 ### Field template
diff --git a/playground/app.js b/playground/app.js
index c86616b99..bf4f868c3 100644
--- a/playground/app.js
+++ b/playground/app.js
@@ -21,7 +21,13 @@ import "codemirror/theme/eclipse.css";
 const log = type => console.log.bind(console, type);
 const fromJson = json => JSON.parse(json);
 const toJson = val => JSON.stringify(val, null, 2);
-const liveValidateSchema = { type: "boolean", title: "Live validation" };
+const liveSettingsSchema = {
+  type: "object",
+  properties: {
+    validate: { type: "boolean", title: "Live validation" },
+    disable: { type: "boolean", title: "Disable whole form" },
+  },
+};
 const cmOptions = {
   theme: "default",
   height: "auto",
@@ -323,7 +329,10 @@ class App extends Component {
       validate,
       editor: "default",
       theme: "default",
-      liveValidate: true,
+      liveSettings: {
+        validate: true,
+        disable: false,
+      },
       shareURL: null,
     };
   }
@@ -373,7 +382,7 @@ class App extends Component {
     });
   };
 
-  setLiveValidate = ({ formData }) => this.setState({ liveValidate: formData });
+  setLiveSettings = ({ formData }) => this.setState({ liveSettings: formData });
 
   onFormDataChange = ({ formData }) =>
     this.setState({ formData, shareURL: null });
@@ -396,7 +405,7 @@ class App extends Component {
       schema,
       uiSchema,
       formData,
-      liveValidate,
+      liveSettings,
       validate,
       theme,
       editor,
@@ -415,9 +424,9 @@ class App extends Component {
             </div>
             <div className="col-sm-2">
               <Form
-                schema={liveValidateSchema}
-                formData={liveValidate}
-                onChange={this.setLiveValidate}>
+                schema={liveSettingsSchema}
+                formData={liveSettings}
+                onChange={this.setLiveSettings}>
                 <div />
               </Form>
             </div>
@@ -457,7 +466,8 @@ class App extends Component {
             <Form
               ArrayFieldTemplate={ArrayFieldTemplate}
               ObjectFieldTemplate={ObjectFieldTemplate}
-              liveValidate={liveValidate}
+              liveValidate={liveSettings.validate}
+              disabled={liveSettings.disable}
               schema={schema}
               uiSchema={uiSchema}
               formData={formData}
diff --git a/src/components/Form.js b/src/components/Form.js
index b233621b1..0a7126827 100644
--- a/src/components/Form.js
+++ b/src/components/Form.js
@@ -18,6 +18,7 @@ export default class Form extends Component {
     uiSchema: {},
     noValidate: false,
     liveValidate: false,
+    disabled: false,
     safeRenderCompletion: false,
     noHtml5Validate: false,
     ErrorList: DefaultErrorList,
@@ -32,6 +33,7 @@ export default class Form extends Component {
     ) {
       this.props.onChange(this.state);
     }
+    this.formElement = null;
   }
 
   componentWillReceiveProps(nextProps) {
@@ -185,6 +187,12 @@ export default class Form extends Component {
     };
   }
 
+  submit() {
+    if (this.formElement) {
+      this.formElement.dispatchEvent(new Event("submit"));
+    }
+  }
+
   render() {
     const {
       children,
@@ -200,6 +208,7 @@ export default class Form extends Component {
       enctype,
       acceptcharset,
       noHtml5Validate,
+      disabled,
     } = this.props;
 
     const { schema, uiSchema, formData, errorSchema, idSchema } = this.state;
@@ -218,7 +227,10 @@ export default class Form extends Component {
         encType={enctype}
         acceptCharset={acceptcharset}
         noValidate={noHtml5Validate}
-        onSubmit={this.onSubmit}>
+        onSubmit={this.onSubmit}
+        ref={form => {
+          this.formElement = form;
+        }}>
         {this.renderErrors()}
         <_SchemaField
           schema={schema}
@@ -232,6 +244,7 @@ export default class Form extends Component {
           onFocus={this.onFocus}
           registry={registry}
           safeRenderCompletion={safeRenderCompletion}
+          disabled={disabled}
         />
         {children ? (
           children


Test patch:
diff --git a/test/Form_test.js b/test/Form_test.js
index d90de7aed..630026a82 100644
--- a/test/Form_test.js
+++ b/test/Form_test.js
@@ -1746,6 +1746,33 @@ describe("Form", () => {
     });
   });
 
+  describe("Form disable prop", () => {
+    const schema = {
+      type: "object",
+      properties: {
+        foo: { type: "string" },
+        bar: { type: "string" },
+      },
+    };
+    const formData = { foo: "foo", bar: "bar" };
+
+    it("should enable all items", () => {
+      const { node } = createFormComponent({ schema, formData });
+
+      expect(node.querySelectorAll("input:disabled")).to.have.length.of(0);
+    });
+
+    it("should disable all items", () => {
+      const { node } = createFormComponent({
+        schema,
+        formData,
+        disabled: true,
+      });
+
+      expect(node.querySelectorAll("input:disabled")).to.have.length.of(2);
+    });
+  });
+
   describe("Attributes", () => {
     const formProps = {
       schema: {},