# react-schema-form-generator
[![Build Status](https://travis-ci.org/xubuild/react-schema-form-generator.svg?branch=master)](https://travis-ci.org/xubuild/react-schema-form-generator)
[![codecov.io](https://codecov.io/github/xubuild/react-schema-form-generator/coverage.svg?branch=master)](https://codecov.io/github/xubuild/react-schema-form-generator?branch=master)

## 【Demo](https://xubuild.github.io/react-schema-form-generator/)

## Introduction

The purpose of this tool is to generate a React form from a schema. The initial use is to generate config form for different chart libraries.

It is inspired by [react-form-generator](https://github.com/SteveVitali/react-form-generator). I decide to build another one since I want to:

- isolate the input components so it can use other UI framework (like material-ui)
- rewrite in ES6 and restructure the project
- add initial value of the form as another input

## Use
```
import {SchemaForm} from 'react-schema-form-generator';

// validation methods
function isRequired(val) {
  if (!val) {
    return "this field is required";
  }
}

function between1and10(val) {
  if (val && (val > 10 || val < 1)) {
    return "need to be >1 and <10";
  }
}

function hasMax3Child(size) {
  if (size && size > 3) {
    return "max 3 child in array";
  }
}

// schema
const schema = {
  selectField: {
    schema: String,
    label: 'select Field',
    options: ["A", "B"],
    defaultVal: "A"
  },
  booleanField: {
    schema: Boolean,
    label: 'booleanField',
    defaultVal: false
  },
  numberField: {
    schema: Number,
    label: 'Number Field',
    defaultVal: 8,
    validate: between1and10
  },
  stringField: {
    schema: String,
    label: 'string field',
    defaultVal: "",
    validate: isRequired
  },
  stringArray: {
    schema: [String],
    label: 'this is a string array',
    defaultVal: ['default array value 1', 'default array value 2']
  },
  objectField: {
    schema: {
      objectPropString: {
        schema: String,
        label: 'object Prop String',
        defaultVal: 'default value'
      },
      objectPropArray: {
        schema: [String],
        label: 'object Prop Array',
        defaultVal: ['array value 1', 'array value 2'],
        validate: hasMax3Child
      },
      objectPropArrayOfObjects: {
        schema: [{
          objectPropString: {
            schema: String,
            label: 'object Prop String',
            defaultVal: "default object prop string"
          },
          objectPropNum: {
            schema: Number,
            label: 'object Prop Num',
            defaultVal: 90
          }
        }],
        label: 'object Prop Array Of Objects',
        defaultVal: [{objectPropString: "obj1", objectPropNum: 1}, {objectPropString: "obj2"}]
      }
    },
    label: 'Object Field'
  },
  nestedFieldL1: {
    schema: {
      nestedFieldL2: {
        schema: [{
          nestedFieldL3: {
            schema: {
              nestedFieldL4: {
                schema: [{
                  nestedFieldL5: {
                    schema: String,
                    label: 'nestedFieldL5',
                    defaultVal: "default object prop string"
                  },
                  nestedFieldL5_2: {
                    schema: Number,
                    label: 'nestedFieldL5_2',
                    defaultVal: 91
                  }
                }],
                label: 'nestedFieldL4',
              }
            },
            label: 'nestedFieldL3',
            defaultVal: "default object prop string"
          },
          nestedFieldL3_2: {
            schema: Number,
            label: 'nestedFieldL3_2',
            defaultVal: 90
          }
        }],
        label: 'nestedFieldL2',
      }
    },
    label: 'nested Field L1'
  }
};

const val = {
  "selectField": {_val: "qq2", _options: [{val: "qq", text: "qq"}, {val: "qq2", text: "qq2"}]},
  "stringField": "sth",
  "booleanField": true,
  "numberField": 99,
  "stringArray": [
    "1",
    "2"
  ],
  "objectField": {
    "objectPropString": "hi",
    "objectPropArray": [
      "hi 1",
      "hi 2"
    ],
    "objectPropArrayOfObjects": [
      {
        "objectPropString": "hello",
        "objectPropNum": 100
      },
      {
        "objectPropString": "hello2",
        "objectPropNum": 101
      }
    ]
  },
  nestedFieldL1: {
    nestedFieldL2: [{
      nestedFieldL3: {
        nestedFieldL4: [{
          nestedFieldL5: "l5",
          nestedFieldL5_2: 52
        }]
      },
      nestedFieldL3_2: 32
    }]
  }
};

// render

ReactDOM.render(
  <div>
    <h2>default input example:</h2>
    <hr/>
    <SchemaForm
      schema={schema}
      val={val}
      onSubmit={(formObj)=>console.log(formObj)}
    />
    <hr/>
    <h2>material-ui input example:</h2>
    <hr/>
    <div className="material-ui">
      <SchemaForm
        addBtn={MButtonCreator('add')}
        delBtn={MButtonCreator('delete')}
        submitBtn={MButtonCreator('submit')}
        textInput={MTextInput}
        numInput={MTextInput}
        selectInput={MSelect}
        boolInput={MBool}
        schema={schema}
        val={val}
        onSubmit={(formObj)=>console.log(formObj)}
      />
    </div>

  </div>
  ,
  document.getElementById('root')
);
```

## Documentation

`SchemaForm` will take 3 props, `schema` defines the form, `val` is the initial value of the form, and `onSubmit` is the callback when you click submit. The parameter of `onSubmit` is a object that represents the form.

### schema

`schema` is an object that defines a form. Each property of this object will be a property in the final form result object. Each property should be an object which can contain the following:

- `schema` (mandatory): the schema for this form field. It can be:
    + `String`
    + `Number`
    + an object, which should have its own `schema`, `label`, etc.
    + [`String`] or [`Number`] or [{}]. For array of objects, just provide the first object.
- (optional) `label`: displayed text for this form field
- (optional) `defaultVal`: default value for this form field.
    + If `schema` is object, this field will not work, `defaultVal` should be set inside of the `schema` object.
- (optional) `validate`: validate function like `(input)=>{}`. It should return `null` if valid, an error message if invalid.
    + if `schema` is `String` or `Number`, `input` is the value of input
    + if `schema` is array, `input` is the size of the array
    + if `schema` is object, this field will not work
- (optional) `options`: if `schema` is `String` or `Number`, this can take a array to display a select.
- (optional) `noAddButton` and `noDelButton`: if `schema` is array, hide add button and delete button.

### val

`val` is the initial value of the form. It will override the defaultVal.

### onSubmit

`onSubmit` is a callback function (`(formObj)={}`) which will be called when clicking the submit button AND validation passes. `formObj` will be an object representing the form.

## Use other input components

Other React component can be passed as `SchemaForm`'s `props`, which will replace the default inputs and buttons.


```
<SchemaForm
  addBtn={MButtonCreator('add')}
  delBtn={MButtonCreator('delete')}
  submitBtn={MButtonCreator('submit')}
  textInput={MTextInput}
  numInput={MTextInput}
  selectInput={MSelect}
  boolInput={MBool}
  schema={schema}
  val={val}
  onSubmit={(formObj)=>console.log(formObj)}
/>
```
The new component should take certain `props`. This is an example of using material-ui in `/src/components/material_ui`.

## TODO:
[x] basic form
[x] select
[x] isolate the input components
[x] default value
[x] validation
[x] test case
[x] doc
[x] publish
[x] use another way to register components
[ ] style
