diff --git a/README.md b/README.md index 2e4c652844..f348979bd5 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ A default, very basic CSS stylesheet is provided, though you're encouraged to bu ## Usage -```js +```jsx import React, { Component } from "react"; import { render } from "react-dom"; @@ -79,7 +79,7 @@ JSONSchema is limited for describing how a given data type should be rendered as Example: -```js +```jsx const uiSchema =  { done: { widget: "radio" // could also be "select" @@ -119,7 +119,7 @@ Here's a list of supported alternative widgets for different JSONSchema data typ The UISchema object accepts a `classNames` property for each field of the schema: -```js +```jsx const uiSchema = { title: { classNames: "task-title foo-bar" @@ -148,7 +148,7 @@ You can provide your own custom widgets to a uiSchema for the following json dat - `boolean` - `date-time` -```js +```jsx const schema = { type: "string" }; @@ -179,6 +179,36 @@ The following props are passed to the widget component: - `placeholder`: The placeholder value, if any; - `options`: The list of options for `enum` fields; +## Custom SchemaField + +**Warning:** This is a powerful feature as you can override the whole form behavior and easily mess it up. Handle with care. + +You can provide your own implementation of the `SchemaField` base React component for rendering any JSONSchema field type, including objects and arrays. This is useful when you want to augment a given field type with supplementary powers. + +To proceed so, you can pass a `SchemaField` prop to the `Form` component instance; here's a rather silly example wrapping the standard `SchemaField` lib component: + +```jsx +import SchemaField from "react-jsonschema-form/lib/components/fields/SchemaField"; + +const CustomSchemaField = function(props) { + return ( +
+

Yeah, I'm pretty dumb.

+ +
+ ); +}; + +render(( +
+), document.getElementById("app")); +``` + +If you're curious how this could ever be useful, have a look at the [Kinto formbuilder](https://github.com/Kinto/formbuilder) repository to see how it's used to provide editing capabilities to any form field. + ## Development server ``` diff --git a/src/components/Form.js b/src/components/Form.js index 4298769702..0df9cac4d6 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -1,11 +1,9 @@ import React, { Component, PropTypes } from "react"; import { Validator } from "jsonschema"; - -import { getDefaultFormState } from "../utils"; import SchemaField from "./fields/SchemaField"; +import { getDefaultFormState } from "../utils"; import ErrorList from "./ErrorList"; - export default class Form extends Component { static defaultProps = { uiSchema: {} @@ -78,14 +76,16 @@ export default class Form extends Component { render() { const {schema, uiSchema} = this.props; const {formData} = this.state; + const _SchemaField = this.props.SchemaField || SchemaField; return ( {this.renderErrors()} - + onChange={this.onChange.bind(this)} + SchemaField={_SchemaField}/>

); @@ -100,6 +100,7 @@ if (process.env.NODE_ENV !== "production") { onChange: PropTypes.func, onError: PropTypes.func, onSubmit: PropTypes.func, + SchemaField: PropTypes.func, }; } diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js index 9b795b8ea0..67b02ca07f 100644 --- a/src/components/fields/ArrayField.js +++ b/src/components/fields/ArrayField.js @@ -1,7 +1,6 @@ import React, { Component, PropTypes } from "react"; import { getDefaultFormState } from "../../utils"; -import SchemaField from "./SchemaField"; class ArrayField extends Component { @@ -65,6 +64,7 @@ class ArrayField extends Component { const {schema, uiSchema, name} = this.props; const title = schema.title || name; const {items} = this.state; + const SchemaField = this.props.SchemaField; return (
@@ -80,7 +80,8 @@ class ArrayField extends Component { uiSchema={uiSchema.items} formData={items[index]} required={this.isItemRequired(schema.items)} - onChange={this.onChange.bind(this, index)} /> + onChange={this.onChange.bind(this, index)} + SchemaField={SchemaField}/>

@@ -102,6 +103,7 @@ if (process.env.NODE_ENV !== "production") { uiSchema: PropTypes.object, onChange: PropTypes.func.isRequired, formData: PropTypes.array, + SchemaField: PropTypes.func.isRequired, }; } diff --git a/src/components/fields/BooleanField.js b/src/components/fields/BooleanField.js index 74062e55c8..8a20ed7292 100644 --- a/src/components/fields/BooleanField.js +++ b/src/components/fields/BooleanField.js @@ -1,7 +1,7 @@ import React, { PropTypes } from "react"; import { defaultFieldValue, getAlternativeWidget } from "../../utils"; -import CheckboxField from "./../widgets/CheckboxWidget"; +import CheckboxWidget from "./../widgets/CheckboxWidget"; function BooleanField({schema, name, uiSchema, formData, required, onChange}) { const {title, description} = schema; @@ -19,7 +19,7 @@ function BooleanField({schema, name, uiSchema, formData, required, onChange}) { const Widget = getAlternativeWidget(schema.type, widget); return ; } - return ; + return ; } if (process.env.NODE_ENV !== "production") { diff --git a/src/components/fields/ObjectField.js b/src/components/fields/ObjectField.js index 3558890669..1db56f55b9 100644 --- a/src/components/fields/ObjectField.js +++ b/src/components/fields/ObjectField.js @@ -1,7 +1,6 @@ import React, { Component, PropTypes } from "react"; import { getDefaultFormState } from "../../utils"; -import SchemaField from "./SchemaField"; class ObjectField extends Component { @@ -40,6 +39,7 @@ class ObjectField extends Component { render() { const {schema, uiSchema, name} = this.props; const title = name || schema.title; + const SchemaField = this.props.SchemaField; return (
{title ? {title} : null} @@ -54,7 +54,8 @@ class ObjectField extends Component { schema={schema.properties[name]} uiSchema={uiSchema[name]} formData={this.state[name]} - onChange={this.onChange.bind(this, name)} /> + onChange={this.onChange.bind(this, name)} + SchemaField={SchemaField}/> ); }) }
@@ -69,6 +70,7 @@ if (process.env.NODE_ENV !== "production") { onChange: PropTypes.func.isRequired, formData: PropTypes.object, required: PropTypes.bool, + SchemaField: PropTypes.func.isRequired, }; } diff --git a/test/index_test.js b/test/index_test.js index 10e48c18de..85f3c6fa32 100644 --- a/test/index_test.js +++ b/test/index_test.js @@ -5,6 +5,7 @@ import sinon from "sinon"; import React from "react"; import { Simulate, renderIntoDocument } from "react-addons-test-utils"; import { findDOMNode } from "react-dom"; +import SchemaField from "../src/components/fields/SchemaField"; import Form from "../src"; @@ -44,6 +45,22 @@ describe("Form", () => { }); }); + describe("Custom SchemaField", () => { + const CustomSchemaField = function(props) { + return (
); + }; + + it("should use the specified custom SchemaType property", () => { + const {node} = createComponent({ + schema: {type: "string"}, + SchemaField: CustomSchemaField + }); + + expect(node.querySelectorAll("#custom > .field input[type=text]")) + .to.have.length.of(1); + }); + }); + describe("StringField", () => { describe("TextWidget", () => { it("should render a string field", () => {