Skip to content

Commit 530bdf8

Browse files
committed
Merge pull request rjsf-team#28 from mozilla-services/dynamic-schema-field
Custom schema fields
2 parents 0bdc4dd + 556032c commit 530bdf8

File tree

6 files changed

+67
-15
lines changed

6 files changed

+67
-15
lines changed

README.md

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ A default, very basic CSS stylesheet is provided, though you're encouraged to bu
3737

3838
## Usage
3939

40-
```js
40+
```jsx
4141
import React, { Component } from "react";
4242
import { render } from "react-dom";
4343

@@ -79,7 +79,7 @@ JSONSchema is limited for describing how a given data type should be rendered as
7979

8080
Example:
8181

82-
```js
82+
```jsx
8383
const uiSchema =  {
8484
done: {
8585
widget: "radio" // could also be "select"
@@ -119,7 +119,7 @@ Here's a list of supported alternative widgets for different JSONSchema data typ
119119

120120
The UISchema object accepts a `classNames` property for each field of the schema:
121121

122-
```js
122+
```jsx
123123
const uiSchema = {
124124
title: {
125125
classNames: "task-title foo-bar"
@@ -148,7 +148,7 @@ You can provide your own custom widgets to a uiSchema for the following json dat
148148
- `boolean`
149149
- `date-time`
150150

151-
```js
151+
```jsx
152152
const schema = {
153153
type: "string"
154154
};
@@ -179,6 +179,36 @@ The following props are passed to the widget component:
179179
- `placeholder`: The placeholder value, if any;
180180
- `options`: The list of options for `enum` fields;
181181

182+
## Custom SchemaField
183+
184+
**Warning:** This is a powerful feature as you can override the whole form behavior and easily mess it up. Handle with care.
185+
186+
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.
187+
188+
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:
189+
190+
```jsx
191+
import SchemaField from "react-jsonschema-form/lib/components/fields/SchemaField";
192+
193+
const CustomSchemaField = function(props) {
194+
return (
195+
<div id="custom">
196+
<p>Yeah, I'm pretty dumb.</p>
197+
<SchemaField {...props} />
198+
</div>
199+
);
200+
};
201+
202+
render((
203+
<Form schema={schema}
204+
uiSchema={uiSchema}
205+
formData={formData}
206+
SchemaField={CustomSchemaField} />
207+
), document.getElementById("app"));
208+
```
209+
210+
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.
211+
182212
## Development server
183213

184214
```

src/components/Form.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import React, { Component, PropTypes } from "react";
22
import { Validator } from "jsonschema";
3-
4-
import { getDefaultFormState } from "../utils";
53
import SchemaField from "./fields/SchemaField";
4+
import { getDefaultFormState } from "../utils";
65
import ErrorList from "./ErrorList";
76

8-
97
export default class Form extends Component {
108
static defaultProps = {
119
uiSchema: {}
@@ -78,14 +76,16 @@ export default class Form extends Component {
7876
render() {
7977
const {schema, uiSchema} = this.props;
8078
const {formData} = this.state;
79+
const _SchemaField = this.props.SchemaField || SchemaField;
8180
return (
8281
<form className="rjsf" onSubmit={this.onSubmit.bind(this)}>
8382
{this.renderErrors()}
84-
<SchemaField
83+
<_SchemaField
8584
schema={schema}
8685
uiSchema={uiSchema}
8786
formData={formData}
88-
onChange={this.onChange.bind(this)} />
87+
onChange={this.onChange.bind(this)}
88+
SchemaField={_SchemaField}/>
8989
<p><button type="submit">Submit</button></p>
9090
</form>
9191
);
@@ -100,6 +100,7 @@ if (process.env.NODE_ENV !== "production") {
100100
onChange: PropTypes.func,
101101
onError: PropTypes.func,
102102
onSubmit: PropTypes.func,
103+
SchemaField: PropTypes.func,
103104
};
104105
}
105106

src/components/fields/ArrayField.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { Component, PropTypes } from "react";
22

33
import { getDefaultFormState } from "../../utils";
4-
import SchemaField from "./SchemaField";
54

65

76
class ArrayField extends Component {
@@ -65,6 +64,7 @@ class ArrayField extends Component {
6564
const {schema, uiSchema, name} = this.props;
6665
const title = schema.title || name;
6766
const {items} = this.state;
67+
const SchemaField = this.props.SchemaField;
6868
return (
6969
<fieldset
7070
className={`field field-array field-array-of-${schema.items.type}`}>
@@ -80,7 +80,8 @@ class ArrayField extends Component {
8080
uiSchema={uiSchema.items}
8181
formData={items[index]}
8282
required={this.isItemRequired(schema.items)}
83-
onChange={this.onChange.bind(this, index)} />
83+
onChange={this.onChange.bind(this, index)}
84+
SchemaField={SchemaField}/>
8485
<p className="array-item-remove">
8586
<button type="button"
8687
onClick={this.onDropClick.bind(this, index)}>-</button></p>
@@ -102,6 +103,7 @@ if (process.env.NODE_ENV !== "production") {
102103
uiSchema: PropTypes.object,
103104
onChange: PropTypes.func.isRequired,
104105
formData: PropTypes.array,
106+
SchemaField: PropTypes.func.isRequired,
105107
};
106108
}
107109

src/components/fields/BooleanField.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { PropTypes } from "react";
22

33
import { defaultFieldValue, getAlternativeWidget } from "../../utils";
4-
import CheckboxField from "./../widgets/CheckboxWidget";
4+
import CheckboxWidget from "./../widgets/CheckboxWidget";
55

66
function BooleanField({schema, name, uiSchema, formData, required, onChange}) {
77
const {title, description} = schema;
@@ -19,7 +19,7 @@ function BooleanField({schema, name, uiSchema, formData, required, onChange}) {
1919
const Widget = getAlternativeWidget(schema.type, widget);
2020
return <Widget options={[true, false]} {... commonProps} />;
2121
}
22-
return <CheckboxField {...commonProps} />;
22+
return <CheckboxWidget {...commonProps} />;
2323
}
2424

2525
if (process.env.NODE_ENV !== "production") {

src/components/fields/ObjectField.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { Component, PropTypes } from "react";
22

33
import { getDefaultFormState } from "../../utils";
4-
import SchemaField from "./SchemaField";
54

65

76
class ObjectField extends Component {
@@ -40,6 +39,7 @@ class ObjectField extends Component {
4039
render() {
4140
const {schema, uiSchema, name} = this.props;
4241
const title = name || schema.title;
42+
const SchemaField = this.props.SchemaField;
4343
return (
4444
<fieldset>
4545
{title ? <legend>{title}</legend> : null}
@@ -54,7 +54,8 @@ class ObjectField extends Component {
5454
schema={schema.properties[name]}
5555
uiSchema={uiSchema[name]}
5656
formData={this.state[name]}
57-
onChange={this.onChange.bind(this, name)} />
57+
onChange={this.onChange.bind(this, name)}
58+
SchemaField={SchemaField}/>
5859
);
5960
})
6061
}</fieldset>
@@ -69,6 +70,7 @@ if (process.env.NODE_ENV !== "production") {
6970
onChange: PropTypes.func.isRequired,
7071
formData: PropTypes.object,
7172
required: PropTypes.bool,
73+
SchemaField: PropTypes.func.isRequired,
7274
};
7375
}
7476

test/index_test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import sinon from "sinon";
55
import React from "react";
66
import { Simulate, renderIntoDocument } from "react-addons-test-utils";
77
import { findDOMNode } from "react-dom";
8+
import SchemaField from "../src/components/fields/SchemaField";
89

910
import Form from "../src";
1011

@@ -44,6 +45,22 @@ describe("Form", () => {
4445
});
4546
});
4647

48+
describe("Custom SchemaField", () => {
49+
const CustomSchemaField = function(props) {
50+
return (<div id="custom"><SchemaField {...props} /></div>);
51+
};
52+
53+
it("should use the specified custom SchemaType property", () => {
54+
const {node} = createComponent({
55+
schema: {type: "string"},
56+
SchemaField: CustomSchemaField
57+
});
58+
59+
expect(node.querySelectorAll("#custom > .field input[type=text]"))
60+
.to.have.length.of(1);
61+
});
62+
});
63+
4764
describe("StringField", () => {
4865
describe("TextWidget", () => {
4966
it("should render a string field", () => {

0 commit comments

Comments
 (0)