Skip to content

Commit f24a728

Browse files
olzraitin1k0
authored andcommitted
Allow overriding the default fields/widgets (rjsf-team#371)
1 parent b5b6aaf commit f24a728

17 files changed

+366
-107
lines changed

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
5959
- [The formContext object](#the-formcontext-object)
6060
- [Custom array field buttons](#custom-array-field-buttons)
6161
- [Custom SchemaField](#custom-schemafield)
62+
- [Customizing the fields and widgets](#customizing-the-default-fields-and-widgets)
6263
- [Custom titles](#custom-titles)
6364
- [Custom descriptions](#custom-descriptions)
6465
- [Form data validation](#form-data-validation)
@@ -952,6 +953,49 @@ If you're curious how this could ever be useful, have a look at the [Kinto formb
952953

953954
Props passed to a custom SchemaField are the same as [the ones passed to a custom field](#field-props).
954955

956+
### Customizing the default fields and widgets
957+
958+
You can override any default field and widget, including the internal widgets like the `CheckboxWidget` that `ObjectField` renders for boolean values. You can override any field and widget just by providing the customized fields/widgets in the `fields` and `widgets` props:
959+
960+
```jsx
961+
962+
const CustomCheckbox = function(props) {
963+
return (
964+
<button id="custom" className={props.value ? "checked" : "unchecked"} onClick={props.onChange(!props.value)}>
965+
{props.value}
966+
</button>
967+
);
968+
};
969+
970+
const widgets = {
971+
CheckboxWidget: CustomCheckbox
972+
};
973+
974+
render((
975+
<Form schema={schema}
976+
uiSchema={uiSchema}
977+
formData={formData}
978+
widgets={widgets} />
979+
), document.getElementById("app"));
980+
```
981+
982+
This allows you to create a reusable customized form class with your custom fields and widgets:
983+
984+
```jsx
985+
const customFields = {StringField: CustomString};
986+
const customWidgets = {CheckboxWidget: CustomCheckbox};
987+
988+
function MyForm(props) {
989+
return <Form fields={customFields} widgets={customWidgets} {...props} />;
990+
}
991+
992+
render((
993+
<MyForm schema={schema}
994+
uiSchema={uiSchema}
995+
formData={formData} />
996+
), document.getElementById("app"));
997+
```
998+
955999
### Custom titles
9561000

9571001
You can provide your own implementation of the `TitleField` base React component for rendering any title. This is useful when you want to augment how titles are handled.

src/components/Form.js

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

3-
import SchemaField from "./fields/SchemaField";
4-
import TitleField from "./fields/TitleField";
5-
import DescriptionField from "./fields/DescriptionField";
6-
73
import ErrorList from "./ErrorList";
84
import {
95
getDefaultFormState,
106
shouldRender,
117
toIdSchema,
128
setState,
9+
getDefaultRegistry,
1310
} from "../utils";
1411
import validateFormData from "../validate";
1512

@@ -118,19 +115,11 @@ export default class Form extends Component {
118115
getRegistry() {
119116
// For BC, accept passed SchemaField and TitleField props and pass them to
120117
// the "fields" registry one.
121-
const _SchemaField = this.props.SchemaField || SchemaField;
122-
const _TitleField = this.props.TitleField || TitleField;
123-
const _DescriptionField = this.props.DescriptionField || DescriptionField;
124-
125-
const fields = Object.assign({
126-
SchemaField: _SchemaField,
127-
TitleField: _TitleField,
128-
DescriptionField: _DescriptionField,
129-
}, this.props.fields);
118+
const {fields, widgets} = getDefaultRegistry();
130119
return {
131-
fields,
120+
fields: {...fields, ...this.props.fields},
121+
widgets: {...widgets, ...this.props.widgets},
132122
FieldTemplate: this.props.FieldTemplate,
133-
widgets: this.props.widgets || {},
134123
definitions: this.props.schema.definitions || {},
135124
formContext: this.props.formContext || {},
136125
};

src/components/fields/ArrayField.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import {
1414
getDefaultRegistry,
1515
setState
1616
} from "../../utils";
17-
import FileWidget from "./../widgets/FileWidget";
18-
1917

2018
function ArrayFieldTitle({TitleField, idSchema, title, required}) {
2119
if (!title) {
@@ -251,6 +249,7 @@ class ArrayField extends Component {
251249
const {schema, idSchema, name, disabled, readonly, autofocus} = this.props;
252250
const title = schema.title || name;
253251
const {items} = this.state;
252+
const {FileWidget} = this.props.registry.widgets;
254253
return (
255254
<FileWidget
256255
id={idSchema && idSchema.$id}

src/components/fields/BooleanField.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
getDefaultRegistry,
88
isObject,
99
} from "../../utils";
10-
import CheckboxWidget from "./../widgets/CheckboxWidget";
1110

1211

1312
function buildOptions(schema, uiWidget) {
@@ -58,6 +57,7 @@ function BooleanField(props) {
5857
const Widget = getAlternativeWidget(schema, widget, widgets);
5958
return <Widget options={buildOptions(schema, uiSchema["ui:widget"])} {...commonProps}/>;
6059
}
60+
const {CheckboxWidget} = registry.widgets;
6161
return <CheckboxWidget {...commonProps}/>;
6262
}
6363

src/components/fields/NumberField.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import React, {PropTypes} from "react";
22

33
import {asNumber} from "../../utils";
4-
import StringField from "./StringField";
54

65
function NumberField(props) {
6+
const {StringField} = props.registry.fields;
77
return (
88
<StringField {...props}
99
onChange={(value) => props.onChange(asNumber(value))}/>

src/components/fields/SchemaField.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,16 @@ import {
66
getDefaultRegistry,
77
isFilesArray
88
} from "../../utils";
9-
import ArrayField from "./ArrayField";
10-
import BooleanField from "./BooleanField";
11-
import NumberField from "./NumberField";
12-
import ObjectField from "./ObjectField";
13-
import StringField from "./StringField";
149
import UnsupportedField from "./UnsupportedField";
1510

1611
const REQUIRED_FIELD_SYMBOL = "*";
1712
const COMPONENT_TYPES = {
18-
array: ArrayField,
19-
boolean: BooleanField,
20-
integer: NumberField,
21-
number: NumberField,
22-
object: ObjectField,
23-
string: StringField,
13+
array: "ArrayField",
14+
boolean: "BooleanField",
15+
integer: "NumberField",
16+
number: "NumberField",
17+
object: "ObjectField",
18+
string: "StringField",
2419
};
2520

2621
function getFieldComponent(schema, uiSchema, fields) {
@@ -31,7 +26,8 @@ function getFieldComponent(schema, uiSchema, fields) {
3126
if (typeof field === "string" && field in fields) {
3227
return fields[field];
3328
}
34-
return COMPONENT_TYPES[schema.type] || UnsupportedField;
29+
const componentName = COMPONENT_TYPES[schema.type];
30+
return componentName in fields ? fields[componentName] : UnsupportedField;
3531
}
3632

3733
function Label(props) {

src/components/fields/StringField.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import {
66
optionsList,
77
getDefaultRegistry
88
} from "../../utils";
9-
import TextWidget from "../widgets/TextWidget";
10-
import SelectWidget from "../widgets/SelectWidget";
119

1210

1311
function StringField(props) {
@@ -39,7 +37,11 @@ function StringField(props) {
3937
readonly,
4038
formContext,
4139
autofocus,
40+
registry,
4241
};
42+
43+
const {TextWidget, SelectWidget} = widgets;
44+
4345
if (Array.isArray(schema.enum)) {
4446
const enumOptions = optionsList(schema);
4547
if (widget) {

src/components/widgets/AltDateTimeWidget.js

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

3-
import AltDateWidget from "./AltDateWidget";
4-
53

64
function AltDateTimeWidget(props) {
5+
const {AltDateWidget} = props.registry.widgets;
76
return <AltDateWidget time {...props}/>;
87
}
98

src/components/widgets/AltDateWidget.js

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

33
import {shouldRender, parseDateString, toDateString, pad} from "../../utils";
4-
import SelectWidget from "../widgets/SelectWidget";
54

65

76
function rangeOptions(type, start, stop) {
@@ -17,8 +16,9 @@ function readyForChange(state) {
1716
}
1817

1918
function DateElement(props) {
20-
const {type, range, value, select, rootId, disabled, readonly, autofocus} = props;
19+
const {type, range, value, select, rootId, disabled, readonly, autofocus, registry} = props;
2120
const id = rootId + "_" + type;
21+
const {SelectWidget} = registry.widgets;
2222
return (
2323
<SelectWidget
2424
schema={{type: "integer"}}
@@ -101,7 +101,7 @@ class AltDateWidget extends Component {
101101
}
102102

103103
render() {
104-
const {id, disabled, readonly, autofocus} = this.props;
104+
const {id, disabled, readonly, autofocus, registry} = this.props;
105105
return (
106106
<ul className="list-inline">{
107107
this.dateElementProps.map((elemProps, i) => (
@@ -112,6 +112,7 @@ class AltDateWidget extends Component {
112112
{...elemProps}
113113
disabled= {disabled}
114114
readonly={readonly}
115+
registry={registry}
115116
autofocus={autofocus && i === 0}/>
116117
</li>
117118
))

src/components/widgets/BaseInput.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function BaseInput(props) {
1212
options, // eslint-disable-line
1313
schema, // eslint-disable-line
1414
formContext, // eslint-disable-line
15+
registry, // eslint-disable-line
1516
...inputProps
1617
} = props;
1718
return (

0 commit comments

Comments
 (0)