diff --git a/.gitignore b/.gitignore
index 0a9d6e535c..78b439b056 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
npm-debug.log
node_modules
+build
lib
diff --git a/devServer.js b/devServer.js
index b2029fc639..f8ee35eb66 100644
--- a/devServer.js
+++ b/devServer.js
@@ -16,7 +16,7 @@ app.use(require("webpack-dev-middleware")(compiler, {
app.use(require("webpack-hot-middleware")(compiler));
app.get("/", function(req, res) {
- res.sendFile(path.join(__dirname, "index.html"));
+ res.sendFile(path.join(__dirname, "playground", "index.html"));
});
app.get("/react-jsonschema-form.css", function(req, res) {
diff --git a/index.html b/index.html
deleted file mode 100644
index 241963df06..0000000000
--- a/index.html
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
- React Component Playground
-
-
-
-
-
-
-
-
diff --git a/package.json b/package.json
index 399df5572d..8b70645203 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,10 @@
"build:css": "cp css/react-jsonschema-form.css dist/",
"build:lib": "rimraf lib && babel -d lib/ src/",
"build:dist": "rimraf dist && webpack --config webpack.config.dist.js --optimize-minimize",
+ "build:playground": "rimraf build && webpack --config webpack.config.prod.js --optimize-minimize && cp playground/index.prod.html build/index.html",
"dist": "npm run build:lib && npm run build:dist && npm run build:css",
"lint": "eslint src test",
+ "publish-to-gh-pages": "npm run build:playground && gh-pages --dist build/",
"publish-to-npm": "npm run dist && npm publish",
"start": "node devServer.js",
"tdd": "npm run test -- -w",
@@ -31,20 +33,24 @@
"babel-loader": "^5.3.2",
"babel-plugin-react-transform": "^1.1.1",
"chai": "^3.3.0",
- "cosmos-js": "^0.7.1",
+ "codemirror": "^5.10.0",
+ "css-loader": "^0.23.1",
"eslint": "^1.8.0",
"eslint-plugin-react": "^3.6.3",
"express": "^4.13.3",
+ "extract-text-webpack-plugin": "^0.9.1",
"gh-pages": "^0.4.0",
"html": "0.0.10",
"jsdom": "^7.2.1",
"mocha": "^2.3.0",
"react-addons-test-utils": "^0.14.3",
+ "react-codemirror": "^0.2.2",
"react-transform-catch-errors": "^1.0.0",
"react-transform-hmr": "^1.0.1",
"redbox-react": "^1.2.0",
"rimraf": "^2.4.4",
"sinon": "^1.17.2",
+ "style-loader": "^0.13.0",
"webpack": "^1.10.5",
"webpack-dev-middleware": "^1.4.0",
"webpack-hot-middleware": "^2.6.0"
diff --git a/playground/app.js b/playground/app.js
new file mode 100644
index 0000000000..14ea95bdb4
--- /dev/null
+++ b/playground/app.js
@@ -0,0 +1,154 @@
+import React, { Component } from "react";
+import { render } from "react-dom";
+import Codemirror from "react-codemirror";
+import "codemirror/mode/javascript/javascript";
+
+import { samples } from "./samples";
+import Form from "../src";
+
+import "codemirror/lib/codemirror.css";
+import "./styles.css";
+
+const log = (type) => console.log.bind(console, type);
+const fromJson = (json) => JSON.parse(json);
+const toJson = (val) => JSON.stringify(val, null, 2);
+const cmOptions = {
+ theme: "default",
+ height: "auto",
+ viewportMargin: Infinity,
+ mode: {
+ name: "javascript",
+ json: true,
+ statementIndent: 2,
+ },
+ lineNumbers: true,
+ lineWrapping: true,
+ indentWithTabs: false,
+ tabSize: 2,
+};
+
+class Editor extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {valid: true, code: props.code, data: fromJson(props.code)};
+ }
+
+ componentWillReceiveProps(props) {
+ this.setState({code: props.code, data: fromJson(props.code)});
+ }
+
+ onCodeChange(code) {
+ try {
+ this.setState({valid: true, data: fromJson(code), code});
+ this.props.onChange(this.state.data);
+ } catch(err) {
+ this.setState({valid: false, code});
+ }
+ }
+
+ render() {
+ const {title} = this.props;
+ const icon = this.state.valid ? "ok" : "remove";
+ return (
+
+ );
+ }
+}
+
+class Selector extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {current: "Simple"};
+ }
+
+ onClick(label, sampleData, event) {
+ event.preventDefault();
+ this.setState({current: label});
+ this.props.onSelected(sampleData);
+ }
+
+ render() {
+ return (
+ {
+ Object.keys(samples).map((label, i) => {
+ return (
+ -
+
+ {label}
+
+
+ );
+ })
+ }
+ );
+ }
+}
+
+class App extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {form: false, schema: {}, uiSchema: {}, formData: {}};
+ }
+
+ componentDidMount() {
+ this.setState({...samples.Simple, form: true});
+ }
+
+ load(data) {
+ // force resetting form component instance
+ this.setState({form: false},
+ _ => this.setState({...data, form: true}));
+ }
+
+ render() {
+ return (
+
+
+
react-jsonschema-form
+
+
+
+
this.setState({schema})} />
+
+
+ this.setState({uiSchema})} />
+
+
+ this.setState({formData})} />
+
+
+
+
+ {!this.state.form ? null :
+
+
+ );
+ }
+}
+
+render(, document.getElementById("app"));
diff --git a/playground/index.html b/playground/index.html
new file mode 100644
index 0000000000..6cf3415683
--- /dev/null
+++ b/playground/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ react-jsonschema-form playground
+
+
+
+
+
+
+
diff --git a/playground/index.prod.html b/playground/index.prod.html
new file mode 100644
index 0000000000..0472138cee
--- /dev/null
+++ b/playground/index.prod.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ react-jsonschema-form playground
+
+
+
+
+
+
+
+
diff --git a/playground/samples/arrays.js b/playground/samples/arrays.js
new file mode 100644
index 0000000000..5f9b0604ad
--- /dev/null
+++ b/playground/samples/arrays.js
@@ -0,0 +1,11 @@
+module.exports = {
+ schema: {
+ type: "array",
+ items: {
+ type: "string",
+ default: "bazinga"
+ }
+ },
+ uiSchema: {},
+ formData: ["foo", "bar"]
+};
diff --git a/playground/samples/index.js b/playground/samples/index.js
new file mode 100644
index 0000000000..b6673a75a4
--- /dev/null
+++ b/playground/samples/index.js
@@ -0,0 +1,11 @@
+import arrays from "./arrays";
+import nested from "./nested";
+import simple from "./simple";
+import widgets from "./widgets";
+
+export const samples = {
+ Simple: simple,
+ Nested: nested,
+ Arrays: arrays,
+ Widgets: widgets,
+};
diff --git a/playground/samples/nested.js b/playground/samples/nested.js
new file mode 100644
index 0000000000..54da1cac34
--- /dev/null
+++ b/playground/samples/nested.js
@@ -0,0 +1,62 @@
+module.exports = {
+ schema: {
+ title: "A list of tasks",
+ type: "object",
+ required: ["title"],
+ properties: {
+ title: {
+ type: "string",
+ title: "Task list title"
+ },
+ tasks: {
+ type: "array",
+ items: {
+ type: "object",
+ title: "Task",
+ required: ["title"],
+ properties: {
+ title: {
+ type: "string",
+ title: "Title",
+ description: "A sample title"
+ },
+ details: {
+ type: "string",
+ title: "Task details",
+ description: "Enter the task details"
+ },
+ done: {
+ type: "boolean",
+ title: "Done?",
+ default: false
+ }
+ }
+ }
+ }
+ }
+ },
+ uiSchema: {
+ tasks: {
+ items: {
+ details: {
+ widget: "textarea"
+ }
+ }
+ }
+ },
+ formData: {
+ title: "My current tasks",
+ tasks: [
+ {
+ title: "My first task",
+ details: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
+ done: true
+ },
+ {
+ title: "My second task",
+ details: "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur",
+ done: false
+ }
+ ]
+ }
+};
diff --git a/playground/samples/simple.js b/playground/samples/simple.js
new file mode 100644
index 0000000000..660e56f587
--- /dev/null
+++ b/playground/samples/simple.js
@@ -0,0 +1,20 @@
+module.exports = {
+ schema: {
+ title: "A simple todo entry",
+ type: "object",
+ required: ["title"],
+ properties: {
+ title: {type: "string", title: "Title", default: "A new task"},
+ done: {type: "boolean", title: "Done?", default: false}
+ }
+ },
+ uiSchema: {
+ done: {
+ widget: "radio"
+ }
+ },
+ formData: {
+ title: "My task",
+ done: false
+ }
+};
diff --git a/playground/samples/widgets.js b/playground/samples/widgets.js
new file mode 100644
index 0000000000..49d52f97b7
--- /dev/null
+++ b/playground/samples/widgets.js
@@ -0,0 +1,66 @@
+module.exports = {
+ schema: {
+ title: "Widgets",
+ type: "object",
+ properties: {
+ boolean: {
+ type: "object",
+ title: "Boolean field",
+ properties: {
+ default: {
+ type: "boolean",
+ title: "checkbox (default)"
+ },
+ radio: {
+ type: "boolean",
+ title: "radio buttons"
+ },
+ select: {
+ type: "boolean",
+ title: "select box"
+ }
+ }
+ },
+ string: {
+ type: "object",
+ title: "String field",
+ properties: {
+ default: {
+ type: "string",
+ title: "text input (default)"
+ },
+ textarea: {
+ type: "string",
+ title: "textarea"
+ }
+ }
+ }
+ }
+ },
+ uiSchema: {
+ boolean: {
+ radio: {
+ widget: "radio"
+ },
+ select: {
+ widget: "select"
+ }
+ },
+ string: {
+ textarea: {
+ widget: "textarea"
+ }
+ }
+ },
+ formData: {
+ boolean: {
+ default: true,
+ radio: true,
+ select: true
+ },
+ string: {
+ default: "Hello...",
+ textarea: "... World"
+ }
+ }
+};
diff --git a/playground/styles.css b/playground/styles.css
new file mode 100644
index 0000000000..9448f6383d
--- /dev/null
+++ b/playground/styles.css
@@ -0,0 +1,16 @@
+.rjsf label {
+ width: 200px;
+}
+
+.rjsf input[type=text], .rjsf textarea {
+ font-weight: normal;
+ width: 350px;
+}
+
+.rjsf textarea {
+ height: 120px;
+}
+
+.rjsf input[type=radio] {
+ margin-right: .4em;
+}
diff --git a/webpack.config.dev.js b/webpack.config.dev.js
index 82607eeb13..1013dc2876 100644
--- a/webpack.config.dev.js
+++ b/webpack.config.dev.js
@@ -5,14 +5,8 @@ module.exports = {
devtool: "eval",
entry: [
"webpack-hot-middleware/client?reload=true",
- "cosmos-js"
+ "./playground/app"
],
- resolve: {
- alias: {
- COSMOS_COMPONENTS: path.join(__dirname, "src/components"),
- COSMOS_FIXTURES: path.join(__dirname, "fixtures")
- }
- },
output: {
path: path.join(__dirname, "build"),
filename: "bundle.js",
@@ -23,25 +17,40 @@ module.exports = {
new webpack.NoErrorsPlugin()
],
module: {
- loaders: [{
- test: /\.jsx?$/,
- loader: "babel",
- include: path.join(__dirname, "src"),
- query: {
- plugins: ["react-transform"],
- extra: {
- "react-transform": {
- transforms: [{
- transform: "react-transform-hmr",
- imports: ["react"],
- locals: ["module"]
- }, {
- transform: "react-transform-catch-errors",
- imports: ["react", "redbox-react"]
- }]
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ loader: "babel",
+ include: [
+ path.join(__dirname, "src"),
+ path.join(__dirname, "playground"),
+ path.join(__dirname, "node_modules", "codemirror", "mode", "javascript"),
+ ],
+ query: {
+ plugins: ["react-transform"],
+ extra: {
+ "react-transform": {
+ transforms: [{
+ transform: "react-transform-hmr",
+ imports: ["react"],
+ locals: ["module"]
+ }, {
+ transform: "react-transform-catch-errors",
+ imports: ["react", "redbox-react"]
+ }]
+ }
}
}
+ },
+ {
+ test: /\.css$/,
+ loader: "style!css",
+ include: [
+ path.join(__dirname, "css"),
+ path.join(__dirname, "playground"),
+ path.join(__dirname, "node_modules"),
+ ],
}
- }]
+ ]
}
};
diff --git a/webpack.config.prod.js b/webpack.config.prod.js
new file mode 100644
index 0000000000..731c2203f7
--- /dev/null
+++ b/webpack.config.prod.js
@@ -0,0 +1,45 @@
+var path = require("path");
+var webpack = require("webpack");
+var ExtractTextPlugin = require("extract-text-webpack-plugin");
+
+module.exports = {
+ entry: "./playground/app",
+ output: {
+ path: path.join(__dirname, "build"),
+ filename: "bundle.js",
+ publicPath: "/static/"
+ },
+ plugins: [
+ new ExtractTextPlugin("styles.css", {allChunks: true}),
+ new webpack.DefinePlugin({
+ "process.env": {
+ NODE_ENV: JSON.stringify("production")
+ }
+ })
+ ],
+ resolve: {
+ extensions: ["", ".js", ".jsx", ".css"]
+ },
+ module: {
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ loader: "babel",
+ include: [
+ path.join(__dirname, "src"),
+ path.join(__dirname, "playground"),
+ path.join(__dirname, "node_modules", "codemirror", "mode", "javascript"),
+ ],
+ },
+ {
+ test: /\.css$/,
+ loader: ExtractTextPlugin.extract("css-loader"),
+ include: [
+ path.join(__dirname, "css"),
+ path.join(__dirname, "playground"),
+ path.join(__dirname, "node_modules"),
+ ],
+ }
+ ]
+ }
+};