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 ( +
+ + + {" "} + {title} + + +
+ ); + } +} + +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 ( + + ); + } +} + +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 : +
this.setState({formData: data.formData})} + onSubmit={data => this.setState({formData: data.formData})} + onError={log("errors")} />} +
+
+ ); + } +} + +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"), + ], + } + ] + } +};