Skip to content

Commit b00eec0

Browse files
author
Matthew R. Elliott
committed
Merge pull request #3 from TechniqueSoftware/feature/html-tags
HTML Tags
2 parents e91a718 + 508e743 commit b00eec0

File tree

10 files changed

+146
-49
lines changed

10 files changed

+146
-49
lines changed

.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: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ 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

@@ -30,7 +30,7 @@ var ReactJsonSchema = (function () {
3030
value: function parseSchema(schema) {
3131
var element = null;
3232
var elements = null;
33-
if (_lodash2['default'].isArray(schema)) {
33+
if ((0, _lodash.isArray)(schema)) {
3434
elements = this.parseSubSchemas(schema);
3535
} else {
3636
element = this.createComponent(schema);
@@ -43,7 +43,7 @@ var ReactJsonSchema = (function () {
4343
var _this = this;
4444

4545
var Components = [];
46-
_lodash2['default'].forEach(subSchemas, function (subSchema, index) {
46+
(0, _lodash.forEach)(subSchemas, function (subSchema, index) {
4747
subSchema.key = index;
4848
Components.push(_this.parseSchema(subSchema));
4949
});
@@ -52,21 +52,23 @@ var ReactJsonSchema = (function () {
5252
}, {
5353
key: 'createComponent',
5454
value: function createComponent(schema) {
55-
var props = _lodash2['default'].clone(schema);
56-
props = _lodash2['default'].omit(props, ['component', 'children']);
55+
var props = (0, _lodash.clone)(schema);
56+
props = (0, _lodash.omit)(props, ['component', 'children']);
5757
var Component = this.resolveComponent(schema);
58-
var Children = this.resolveComponentChildren(schema);
59-
return _react2['default'].createElement(Component, props, Children);
58+
var Children = props.text || this.resolveComponentChildren(schema);
59+
return (0, _react.createElement)(Component, props, Children);
6060
}
6161
}, {
6262
key: 'resolveComponent',
6363
value: function resolveComponent(schema) {
6464
var Component = null;
65-
if (_lodash2['default'].has(schema, 'component')) {
66-
if (_lodash2['default'].isObject(schema.component)) {
65+
if ((0, _lodash.has)(schema, 'component')) {
66+
if ((0, _lodash.isObject)(schema.component)) {
6767
Component = schema.component;
68-
} else if (_lodash2['default'].isString(schema.component)) {
68+
} else if (_componentMap && _componentMap[schema.component]) {
6969
Component = _componentMap[schema.component];
70+
} else if ((0, _lodash.has)(_node_modulesReactLibReactDOM2['default'], schema.component)) {
71+
Component = schema.component;
7072
}
7173
} else {
7274
throw new Error('ReactJsonSchema could not resolve a component due to a missing component attribute in the schema.');
@@ -76,7 +78,7 @@ var ReactJsonSchema = (function () {
7678
}, {
7779
key: 'resolveComponentChildren',
7880
value: function resolveComponentChildren(schema) {
79-
return _lodash2['default'].has(schema, 'children') ? this.parseSchema(schema.children) : [];
81+
return (0, _lodash.has)(schema, 'children') ? this.parseSchema(schema.children) : [];
8082
}
8183
}, {
8284
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: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
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

@@ -8,7 +9,7 @@ export default class ReactJsonSchema {
89
parseSchema(schema) {
910
let element = null;
1011
let elements = null;
11-
if (_.isArray(schema)) {
12+
if (isArray(schema)) {
1213
elements = this.parseSubSchemas(schema);
1314
} else {
1415
element = this.createComponent(schema);
@@ -18,37 +19,39 @@ export default class ReactJsonSchema {
1819

1920
parseSubSchemas(subSchemas) {
2021
const Components = [];
21-
_.forEach(subSchemas, (subSchema, index) => {
22+
forEach(subSchemas, (subSchema, index) => {
2223
subSchema.key = index;
2324
Components.push(this.parseSchema(subSchema));
2425
});
2526
return Components;
2627
}
2728

2829
createComponent(schema) {
29-
let props = _.clone(schema);
30-
props = _.omit(props, ['component', 'children']);
30+
let props = clone(schema);
31+
props = omit(props, ['component', 'children']);
3132
const Component = this.resolveComponent(schema);
32-
const Children = this.resolveComponentChildren(schema);
33-
return React.createElement(Component, props, Children);
33+
const Children = props.text || this.resolveComponentChildren(schema);
34+
return createElement(Component, props, Children);
3435
}
3536

3637
resolveComponent(schema) {
3738
let Component = null;
38-
if (_.has(schema, 'component')) {
39-
if (_.isObject(schema.component)) {
39+
if (has(schema, 'component')) {
40+
if (isObject(schema.component)) {
4041
Component = schema.component;
41-
} else if (_.isString(schema.component)) {
42+
} else if (_componentMap && _componentMap[schema.component]) {
4243
Component = _componentMap[schema.component];
44+
} else if (has(ReactDOM, schema.component)) {
45+
Component = schema.component;
4346
}
44-
} else {
47+
} else {
4548
throw new Error('ReactJsonSchema could not resolve a component due to a missing component attribute in the schema.');
4649
}
4750
return Component;
4851
}
4952

5053
resolveComponentChildren(schema) {
51-
return (_.has(schema, 'children')) ?
54+
return (has(schema, 'children')) ?
5255
this.parseSchema(schema.children) : [];
5356
}
5457

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)