Skip to content

Commit 829972c

Browse files
committed
Add HTML tag support and update readme
1 parent e91a718 commit 829972c

10 files changed

Lines changed: 152 additions & 49 deletions

File tree

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
spec/spec.js
2+
*.md

README.md

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,122 @@
11
# react-json-schema
22

3-
This library builds React elements from JSON by mapping JSON definitions to React components that you expose. The interest behind making this library is to allow non-programmers whip up a view using JSON, which can be stored and retrieved in a database. Use it as you'd like. (JSX not required)
3+
This library builds React elements from JSON by mapping JSON definitions to React components that you expose. The interest behind making this library is to allow non-programmers to construct a view using JSON, which can be stored and retrieved in a database. Use it as you'd like.
44

5-
### Documentation
5+
JSX is not a dependency for react-json-schema.
6+
7+
For a quick reference, you can jump to [full example](#putting-it-all-together).
68

7-
The first thing you'll need to do is define your schema in JSON (or a JavaScript object literal). When doing so, there are two things to note:
8-
- A **component** key _must_ exist and be defined by a string or React Component
9-
- A **children** key _may_ exist to define sub-components
9+
### Documentation
1010

11-
Next, we have to make sure that react-json-schema can create elements from component definitions. If a schema's **component** is defined by an string, you will need to expose the components included in the schema to react-json-schema. This can be done by calling `setComponentMap` with an object that has keys that match the strings in your schema, to the components are to be resolved by these strings.
11+
#### Schema
1212

13-
Finally, you'll need to call `parseSchema` to create elements from your schema. Now you have React elements at your disposal!
13+
The primary resource needed is a defined schema in JSON or a JavaScript object literal. It's recommended that schema attributes mainly define React component props. The parser explicitly handles the following attributes:
14+
- **component**: _MUST_ exist and be defined by a string or React component (must be a string if describing a native HTML tag)
15+
- **children**: _MAY_ exist to define sub-components
16+
- **text**: _MAY_ exist to as a string to define inner HTML text (overrides children)
1417

15-
Example (taken from /demo/index.jsx)
18+
Example JSON schema (ES6)
1619
```js
1720
const schema = {
1821
"component": "ContactForm",
1922
"title": "Tell us a little about yourself...",
2023
"children": [
2124
{
2225
"component": "StringField",
23-
"label": "What's your name",
24-
"help": "It's okay, don't be shy :)"
26+
"label": "What's your name?"
27+
},
28+
{
29+
"component": "a",
30+
"href": "#faq",
31+
"text": "I'm not sure why I'm filling this out"
32+
}
33+
]
34+
}
35+
```
36+
37+
Example JS literal (ES6)
38+
```js
39+
const schema = {
40+
"component": ContactForm,
41+
"title": "Tell us a little about yourself...",
42+
"children": [
43+
{
44+
"component": StringField,
45+
"label": "What's your name?"
46+
},
47+
{
48+
"component": "a",
49+
"href": "#faq",
50+
"text": "I'm not sure why I'm filling this out"
2551
}
2652
]
2753
}
54+
```
55+
56+
##### Dynamic Children and Keys
57+
58+
When arrays of components exist (like children), react-json-schema will resolve a key for the element based on the array index, which follows the rules for [dynamic children](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children). Custom keys cannot be defined at this time.
2859

29-
/* es6 object literal shorthand */
60+
#### Component Mapping
61+
62+
React components need to be exposed to the react-json-schema so that the parser can create React elements. If the schema contains object literals with component references, the schema is exposing the React components and no additional configuration is needed. If the schema does not contain references to components, the components can be exposed via `setComponentMap`.
63+
64+
Example for exposing non-exposed components (ES6)
65+
```js
66+
/* es6 object literal shorthand: { ContactForm } == { ContactForm: ContactForm } */
67+
contactForm.setComponentMap({ ContactForm, StringField });
68+
```
69+
70+
#### Parsing
71+
72+
Use `parseSchema` to render React elements. It returns the root node. Note that if your schema's root is an array, you'll have to wrap the schema in an element.
73+
74+
Example (ES6)
75+
```js
76+
/* If using ReactDOM (0.14+), else use React */
77+
ReactDOM.render(contactForm.parseSchema(schema),
78+
document.getElementById('contact-form'));
79+
```
80+
81+
##### Rendering
82+
83+
Also note react-json-schema also does not perform any rendering, so the method in which you want to render is up to you. For example, you can use ReactDOMServer.render, ReactDOM.renderToString, etc. if you'd like.
84+
85+
#### Putting it All Together
86+
87+
```js
88+
import ReactDOM from 'react-dom';
89+
import ReactJsonSchema from 'react-json-schema';
90+
91+
import FormStore from './stores/FormStore';
92+
import ContactForm from './components/ContactForm';
93+
import StringField from './components/StringField';
94+
95+
// For this example, let's pretend I already have data and am ignorant of actions
96+
const schema = FormStore.getFormSchema();
3097
const componentMap = { ContactForm, StringField }
98+
3199
const contactForm = new ReactJsonSchema();
32100
contactForm.setComponentMap(componentMap);
33101

34-
React.render(contactForm.parseSchema(schema),
35-
document.getElementById('json-react-schema'));
102+
ReactDOM.render(contactForm.parseSchema(schema),
103+
document.getElementById('contact-form'));
36104
```
37105

38106
### Try the Demo
39107

40108
To run the demo
41-
* have webpack and webpack-dev-server globally installed
42109
* `npm install`
43110
* `npm run demo`
44111
* The app will be served at http://localhost:8080
45112

46113
### Contribution and Code of Conduct
47114

48115
Please use a linter that recognizes eslint rules
49-
50-
* have webpack, webpack-dev-server and jasmine globally installed
51116
* `npm install`
52117
* `npm test` (Jasmine's test report will output in /spec/index.html)
53118
* `npm run build`
54119

55120
### Roadmap
56121

57122
* Support custom keys for children
58-
* Support native html tags as components, with the option to add custom tag definitions
59-
* Drop lodash dependency?

demo/components/StringField.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class StringField extends React.Component {
1818

1919
render() {
2020
return (
21-
<Input type="text" onChange={this.validateInput.bind(this)} {...this.props} />
21+
<Input type="text" onChange={this.validateInput.bind(this)} label={this.props.label} help={this.props.help} />
2222
);
2323
}
2424
}

demo/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
77
</head>
88
<body>
9-
<div id="content"></div>
9+
<div id="welcome-banner"></div>
1010
<div id="json-react-schema"></div>
1111
</body>
1212
<script src="bundle.js"></script>

demo/index.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@ import CheckboxField from './components/CheckboxField';
66
// If a package dependency: import ReactJsonSchema from 'react-json-schema';
77
import ReactJsonSchema from '../dist/react-json-schema';
88

9-
const schema = {
9+
const welcomeSchema = {
10+
'component': 'h2',
11+
'className': 'text-center',
12+
'text': 'Hello World!'
13+
};
14+
15+
const welcomeBanner = new ReactJsonSchema();
16+
React.render(welcomeBanner.parseSchema(welcomeSchema), document.getElementById('welcome-banner'));
17+
18+
const formSchema = {
1019
'component': 'ContactForm',
1120
'title': 'Tell us a little about yourself, we\'d appreciate it',
1221
'children': [
@@ -34,4 +43,4 @@ const componentMap = { ContactForm, StringField, CheckboxField };
3443
const contactForm = new ReactJsonSchema();
3544
contactForm.setComponentMap(componentMap);
3645

37-
React.render(contactForm.parseSchema(schema), document.getElementById('json-react-schema'));
46+
React.render(contactForm.parseSchema(formSchema), document.getElementById('json-react-schema'));

dist/react-json-schema.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,27 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
1212

1313
var _react = require('react');
1414

15-
var _react2 = _interopRequireDefault(_react);
15+
var _node_modulesReactLibReactDOM = require('../node_modules/react/lib/ReactDOM');
1616

17-
var _lodash = require('lodash');
17+
var _node_modulesReactLibReactDOM2 = _interopRequireDefault(_node_modulesReactLibReactDOM);
1818

19-
var _lodash2 = _interopRequireDefault(_lodash);
19+
var _lodash = require('lodash');
2020

2121
var _componentMap = null;
2222

2323
var ReactJsonSchema = (function () {
2424
function ReactJsonSchema() {
2525
_classCallCheck(this, ReactJsonSchema);
26+
27+
console.log(_node_modulesReactLibReactDOM2['default']);
2628
}
2729

2830
_createClass(ReactJsonSchema, [{
2931
key: 'parseSchema',
3032
value: function parseSchema(schema) {
3133
var element = null;
3234
var elements = null;
33-
if (_lodash2['default'].isArray(schema)) {
35+
if ((0, _lodash.isArray)(schema)) {
3436
elements = this.parseSubSchemas(schema);
3537
} else {
3638
element = this.createComponent(schema);
@@ -43,7 +45,7 @@ var ReactJsonSchema = (function () {
4345
var _this = this;
4446

4547
var Components = [];
46-
_lodash2['default'].forEach(subSchemas, function (subSchema, index) {
48+
(0, _lodash.forEach)(subSchemas, function (subSchema, index) {
4749
subSchema.key = index;
4850
Components.push(_this.parseSchema(subSchema));
4951
});
@@ -52,21 +54,23 @@ var ReactJsonSchema = (function () {
5254
}, {
5355
key: 'createComponent',
5456
value: function createComponent(schema) {
55-
var props = _lodash2['default'].clone(schema);
56-
props = _lodash2['default'].omit(props, ['component', 'children']);
57+
var props = (0, _lodash.clone)(schema);
58+
props = (0, _lodash.omit)(props, ['component', 'children']);
5759
var Component = this.resolveComponent(schema);
58-
var Children = this.resolveComponentChildren(schema);
59-
return _react2['default'].createElement(Component, props, Children);
60+
var Children = props.text || this.resolveComponentChildren(schema);
61+
return (0, _react.createElement)(Component, props, Children);
6062
}
6163
}, {
6264
key: 'resolveComponent',
6365
value: function resolveComponent(schema) {
6466
var Component = null;
65-
if (_lodash2['default'].has(schema, 'component')) {
66-
if (_lodash2['default'].isObject(schema.component)) {
67+
if ((0, _lodash.has)(schema, 'component')) {
68+
if ((0, _lodash.isObject)(schema.component)) {
6769
Component = schema.component;
68-
} else if (_lodash2['default'].isString(schema.component)) {
70+
} else if (_componentMap && _componentMap[schema.component]) {
6971
Component = _componentMap[schema.component];
72+
} else if ((0, _lodash.has)(_node_modulesReactLibReactDOM2['default'], schema.component)) {
73+
Component = schema.component;
7074
}
7175
} else {
7276
throw new Error('ReactJsonSchema could not resolve a component due to a missing component attribute in the schema.');
@@ -76,7 +80,7 @@ var ReactJsonSchema = (function () {
7680
}, {
7781
key: 'resolveComponentChildren',
7882
value: function resolveComponentChildren(schema) {
79-
return _lodash2['default'].has(schema, 'children') ? this.parseSchema(schema.children) : [];
83+
return (0, _lodash.has)(schema, 'children') ? this.parseSchema(schema.children) : [];
8084
}
8185
}, {
8286
key: 'getComponentMap',

dist/react-json-schema.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/ReactJsonSchema.js

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import React from 'react';
2-
import _ from 'lodash';
1+
import { createElement } from 'react';
2+
import ReactDOM from '../node_modules/react/lib/ReactDOM';
3+
import { isFunction, isArray, forEach, omit, clone, has, isObject, isString } from 'lodash';
34

45
let _componentMap = null;
56

67
export default class ReactJsonSchema {
78

9+
constructor() {
10+
console.log(ReactDOM);
11+
}
12+
813
parseSchema(schema) {
914
let element = null;
1015
let elements = null;
11-
if (_.isArray(schema)) {
16+
if (isArray(schema)) {
1217
elements = this.parseSubSchemas(schema);
1318
} else {
1419
element = this.createComponent(schema);
@@ -18,37 +23,39 @@ export default class ReactJsonSchema {
1823

1924
parseSubSchemas(subSchemas) {
2025
const Components = [];
21-
_.forEach(subSchemas, (subSchema, index) => {
26+
forEach(subSchemas, (subSchema, index) => {
2227
subSchema.key = index;
2328
Components.push(this.parseSchema(subSchema));
2429
});
2530
return Components;
2631
}
2732

2833
createComponent(schema) {
29-
let props = _.clone(schema);
30-
props = _.omit(props, ['component', 'children']);
34+
let props = clone(schema);
35+
props = omit(props, ['component', 'children']);
3136
const Component = this.resolveComponent(schema);
32-
const Children = this.resolveComponentChildren(schema);
33-
return React.createElement(Component, props, Children);
37+
const Children = props.text || this.resolveComponentChildren(schema);
38+
return createElement(Component, props, Children);
3439
}
3540

3641
resolveComponent(schema) {
3742
let Component = null;
38-
if (_.has(schema, 'component')) {
39-
if (_.isObject(schema.component)) {
43+
if (has(schema, 'component')) {
44+
if (isObject(schema.component)) {
4045
Component = schema.component;
41-
} else if (_.isString(schema.component)) {
46+
} else if (_componentMap && _componentMap[schema.component]) {
4247
Component = _componentMap[schema.component];
48+
} else if (has(ReactDOM, schema.component)) {
49+
Component = schema.component;
4350
}
44-
} else {
51+
} else {
4552
throw new Error('ReactJsonSchema could not resolve a component due to a missing component attribute in the schema.');
4653
}
4754
return Component;
4855
}
4956

5057
resolveComponentChildren(schema) {
51-
return (_.has(schema, 'children')) ?
58+
return (has(schema, 'children')) ?
5259
this.parseSchema(schema.children) : [];
5360
}
5461

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
"name": "elliottisonfire",
1313
"url": "http://elliottisonfire.com"
1414
},
15+
"repository": {
16+
"type" : "git",
17+
"url" : "https://github.com/TechniqueSoftware/react-json-schema"
18+
},
1519
"license": "Apache-2.0",
1620
"bugs": {
1721
"url": "https://github.com/TechniqueSoftware/react-json-schema/issues"
@@ -41,6 +45,7 @@
4145
"eslint-config-airbnb": "^0.1.0",
4246
"eslint-plugin-react": "^3.5.0",
4347
"file-loader": "^0.8.4",
48+
"jasmine": "^2.3.2",
4449
"jsx-loader": "^0.13.2",
4550
"path": "^0.12.7",
4651
"react-bootstrap": "^0.25.2",

0 commit comments

Comments
 (0)