Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) i
- [Advanced customization](#advanced-customization)
- [Field template](#field-template)
- [Array Field Template](#array-field-template)
- [Object Field Template](#object-field-template)
- [Error List template](#error-list-template)
- [Custom widgets and fields](#custom-widgets-and-fields)
- [Custom widget components](#custom-widget-components)
Expand Down Expand Up @@ -840,8 +841,8 @@ Please see [customArray.js](https://github.com/mozilla-services/react-jsonschema

The following props are passed to each `ArrayFieldTemplate`:

- `DescriptionField`: The generated `DescriptionField` (if you wanted to utilize it)
- `TitleField`: The generated `TitleField` (if you wanted to utilize it).
- `DescriptionField`: The `DescriptionField` from the registry (in case you wanted to utilize it)
- `TitleField`: The `TitleField` from the registry (in case you wanted to utilize it).
- `canAdd`: A boolean value stating whether new elements can be added to the array.
- `className`: The className string.
- `disabled`: A boolean value stating if the array is disabled.
Expand Down Expand Up @@ -870,6 +871,51 @@ The following props are part of each element in `items`:
- `onReorderClick: (index, newIndex) => (event) => void`: Returns a function that swaps the items at `index` with `newIndex`.
- `readonly`: A boolean value stating if the array item is readonly.

### Object Field Template

Similarly to the `FieldTemplate` you can use an `ObjectFieldTemplate` to customize how your
objects are rendered.

```jsx
function ObjectFieldTemplate(props) {
return (
<div>
{props.title}
{props.description}
{props.properties.map(element => <div className="property-wrapper">{element.children}</div>)}
</div>
);
}

render((
<Form schema={schema}
ObjectFieldTemplate={ObjectFieldTemplate} />,
), document.getElementById("app"));
```

Please see [customObject.js](https://github.com/mozilla-services/react-jsonschema-form/blob/master/playground/samples/customObject.js) for a better example.

The following props are passed to each `ObjectFieldTemplate`:

- `DescriptionField`: The `DescriptionField` from the registry (in case you wanted to utilize it)
- `TitleField`: The `TitleField` from the registry (in case you wanted to utilize it).
- `title`: A string value containing the title for the object.
- `description`: A string value containing the description for the object.
- `properties`: An array of object representing the properties in the array. Each of the properties represent a child with properties described below.
- `required`: A boolean value stating if the object is required.
- `schema`: The schema object for this object.
- `uiSchema`: The uiSchema object for this object field.
- `idSchema`: An object containing the id for this object & ids for it's properties.
- `formData`: The form data for the object.
- `formContext`: The `formContext` object that you passed to Form.

The following props are part of each element in `properties`:

- `content`: The html for the property's content.
- `name`: A string representing the property name.
- `disabled`: A boolean value stating if the object property is disabled.
- `readonly`: A boolean value stating if the property is readonly.

### Error List template

To take control over how the form errors are displayed, you can define an *error list template* for your form. This list is the form global error list that appears at the top of your forms.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"publish-to-npm": "npm run build:readme && npm run dist && npm publish",
"start": "node devServer.js",
"tdd": "cross-env NODE_ENV=test mocha --compilers js:babel-register --watch --require ./test/setup-jsdom.js test/**/*_test.js",
"test": " cross-env NODE_ENV=test mocha --compilers js:babel-register --require ./test/setup-jsdom.js test/**/*_test.js"
"test": "cross-env NODE_ENV=test mocha --compilers js:babel-register --require ./test/setup-jsdom.js test/**/*_test.js"
},
"prettierOptions": "--jsx-bracket-same-line --trailing-comma es5 --semi",
"lint-staged": {
Expand Down
11 changes: 9 additions & 2 deletions playground/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,15 @@ class App extends Component {

load = data => {
// Reset the ArrayFieldTemplate whenever you load new data
const { ArrayFieldTemplate } = data;
const { ArrayFieldTemplate, ObjectFieldTemplate } = data;
// force resetting form component instance
this.setState({ form: false }, _ =>
this.setState({ ...data, form: true, ArrayFieldTemplate })
this.setState({
...data,
form: true,
ArrayFieldTemplate,
ObjectFieldTemplate,
})
);
};

Expand Down Expand Up @@ -412,6 +417,7 @@ class App extends Component {
theme,
editor,
ArrayFieldTemplate,
ObjectFieldTemplate,
transformErrors,
} = this.state;

Expand Down Expand Up @@ -466,6 +472,7 @@ class App extends Component {
{this.state.form && (
<Form
ArrayFieldTemplate={ArrayFieldTemplate}
ObjectFieldTemplate={ObjectFieldTemplate}
liveValidate={liveValidate}
schema={schema}
uiSchema={uiSchema}
Expand Down
61 changes: 61 additions & 0 deletions playground/samples/customObject.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from "react";

function ObjectFieldTemplate({ TitleField, properties, title, description }) {
return (
<div>
<TitleField title={title} />
<div className="row">
{properties.map(prop => (
<div className="col-lg-2 col-md-4 col-sm-6 col-xs-12">{prop}</div>
))}
</div>
{description}
</div>
);
}

module.exports = {
schema: {
title: "A registration form",
description:
"This is the same as the simple form, but it is rendered as a bootstrap grid. Try shrinking the browser window to see it in action.",
type: "object",
required: ["firstName", "lastName"],
properties: {
firstName: {
type: "string",
title: "First name",
},
lastName: {
type: "string",
title: "Last name",
},
age: {
type: "integer",
title: "Age",
},
bio: {
type: "string",
title: "Bio",
},
password: {
type: "string",
title: "Password",
minLength: 3,
},
telephone: {
type: "string",
title: "Telephone",
minLength: 10,
},
},
},
formData: {
firstName: "Chuck",
lastName: "Norris",
age: 75,
bio: "Roundhouse kicking asses since 1940",
password: "noneed",
},
ObjectFieldTemplate,
};
2 changes: 2 additions & 0 deletions playground/samples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import validation from "./validation";
import files from "./files";
import single from "./single";
import customArray from "./customArray";
import customObject from "./customObject";
import alternatives from "./alternatives";

export const samples = {
Expand All @@ -31,5 +32,6 @@ export const samples = {
Files: files,
Single: single,
"Custom Array": customArray,
"Custom Object": customObject,
Alternatives: alternatives,
};
2 changes: 2 additions & 0 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export default class Form extends Component {
fields: { ...fields, ...this.props.fields },
widgets: { ...widgets, ...this.props.widgets },
ArrayFieldTemplate: this.props.ArrayFieldTemplate,
ObjectFieldTemplate: this.props.ObjectFieldTemplate,
FieldTemplate: this.props.FieldTemplate,
definitions: this.props.schema.definitions || {},
formContext: this.props.formContext || {},
Expand Down Expand Up @@ -227,6 +228,7 @@ if (process.env.NODE_ENV !== "production") {
),
fields: PropTypes.objectOf(PropTypes.func),
ArrayFieldTemplate: PropTypes.func,
ObjectFieldTemplate: PropTypes.func,
FieldTemplate: PropTypes.func,
ErrorList: PropTypes.func,
onChange: PropTypes.func,
Expand Down
77 changes: 53 additions & 24 deletions src/components/fields/ObjectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ import {
getDefaultRegistry,
} from "../../utils";

function DefaultObjectFieldTemplate(props) {
const { TitleField, DescriptionField } = props;
return (
<fieldset>
{(props.uiSchema["ui:title"] || props.title) && (
<TitleField
id={`${props.idSchema.$id}__title`}
title={props.title || props.uiSchema["ui:title"]}
required={props.required}
formContext={props.formContext}
/>
)}
{props.description && (
<DescriptionField
id={`${props.idSchema.$id}__description`}
description={props.description}
formContext={props.formContext}
/>
)}
{props.properties.map(prop => prop.content)}
</fieldset>
);
}

class ObjectField extends Component {
static defaultProps = {
uiSchema: {},
Expand Down Expand Up @@ -50,7 +74,9 @@ class ObjectField extends Component {
const { SchemaField, TitleField, DescriptionField } = fields;
const schema = retrieveSchema(this.props.schema, definitions);
const title = schema.title === undefined ? name : schema.title;
const description = uiSchema["ui:description"] || schema.description;
let orderedProperties;

try {
const properties = Object.keys(schema.properties);
orderedProperties = orderProperties(properties, uiSchema["ui:order"]);
Expand All @@ -65,27 +91,19 @@ class ObjectField extends Component {
</div>
);
}
return (
<fieldset>
{(uiSchema["ui:title"] || title) && (
<TitleField
id={`${idSchema.$id}__title`}
title={uiSchema["ui:title"] || title}
required={required}
formContext={formContext}
/>
)}
{(uiSchema["ui:description"] || schema.description) && (
<DescriptionField
id={`${idSchema.$id}__description`}
description={uiSchema["ui:description"] || schema.description}
formContext={formContext}
/>
)}
{orderedProperties.map((name, index) => {
return (

const Template = registry.ObjectFieldTemplate || DefaultObjectFieldTemplate;

const templateProps = {
title: uiSchema["ui:title"] || title,
description,
TitleField,
DescriptionField,
properties: orderedProperties.map(name => {
return {
content: (
<SchemaField
key={index}
key={name}
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
Expand All @@ -100,10 +118,21 @@ class ObjectField extends Component {
disabled={disabled}
readonly={readonly}
/>
);
})}
</fieldset>
);
),
name,
readonly,
disabled,
required,
};
}),
required,
idSchema,
uiSchema,
schema,
formData,
formContext,
};
return <Template {...templateProps} />;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/components/fields/SchemaField.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ if (process.env.NODE_ENV !== "production") {
fields: PropTypes.objectOf(PropTypes.func).isRequired,
definitions: PropTypes.object.isRequired,
ArrayFieldTemplate: PropTypes.func,
ObjectFieldTemplate: PropTypes.func,
FieldTemplate: PropTypes.func,
formContext: PropTypes.object.isRequired,
}),
Expand Down
10 changes: 5 additions & 5 deletions src/components/fields/UnsupportedField.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ function UnsupportedField({ schema, idSchema, reason }) {
<div className="unsupported-field">
<p>
Unsupported field schema{idSchema &&
idSchema.$id && (
<span>
{" for"} field <code>{idSchema.$id}</code>
</span>
)}
idSchema.$id && (
<span>
{" for"} field <code>{idSchema.$id}</code>
</span>
)}
{reason && <em>: {reason}</em>}.
</p>
{schema && <pre>{JSON.stringify(schema, null, 2)}</pre>}
Expand Down
2 changes: 1 addition & 1 deletion test/ArrayFieldTemplate_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("ArrayFieldTemplate", () => {
);
}

describe("Statefull ArrayFieldTemplate", () => {
describe("Stateful ArrayFieldTemplate", () => {
class ArrayFieldTemplate extends PureComponent {
render() {
return <div>{this.props.items.map(item => item.element)}</div>;
Expand Down
Loading