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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ import RcSelect from 'react-schema-form-rc-select/lib/RcSelect';

```

# Error Handler

The error handler is disabled by default but you can enable it by using showErrors prop on `SchemaForm`.

```js
...
onValidate = () => {
if (form is valid) {
...
} else {
this.setState({ showErrors: true });
}
}

...
<>
<SchemaForm
schema={schemaObject}
form={formObject}
model={modelObject}
onModelChange={this.onModelChange}
mapper={mapperObject}
showErrors={this.state.showErrors}
/>
<Button onClick={this.validate}>Submit</Button>
</>
```


# Contributing

Expand Down
68 changes: 51 additions & 17 deletions example/ExamplePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ import {
MenuItem,
Select
} from "@material-ui/core";
import Localizer from "./data/tests/localizer";
import ErrorBoundary from "./ErrorBoundary";
// RcSelect is still in migrating process so it's excluded for now
// import RcSelect from 'react-schema-form-rc-select/lib/RcSelect';
const examples = {
localizer: Localizer
};

class ExamplePage extends React.Component {
type State = {
showErrors: boolean
};

class ExamplePage extends React.Component<void, State> {
tempModel = {
comments: [
{
Expand All @@ -33,7 +41,7 @@ class ExamplePage extends React.Component {
{ label: "Basic JSON Schema Type", value: "data/types.json" },
{ label: "Basic Radios", value: "data/radio.json" },
{ label: "Condition", value: "data/condition.json" },
{ label: "Help", value: "data/help.json"},
{ label: "Help", value: "data/help.json" },
{ label: "Kitchen Sink", value: "data/kitchenSink.json" },
{ label: "Login", value: "data/login.json" },
{ label: "Date", value: "data/date.json" },
Expand All @@ -46,6 +54,10 @@ class ExamplePage extends React.Component {
{
label: "Test - Date Capture",
value: "data/tests/datecapture.json"
},
{
label: "Test - Localizer",
value: "localizer"
}
],
validationResult: {},
Expand All @@ -54,7 +66,9 @@ class ExamplePage extends React.Component {
model: {},
schemaJson: "",
formJson: "",
selected: ""
selected: "",
localization: undefined,
showErrors: false
};

setStateDefault = () => this.setState({ model: this.tempModel });
Expand All @@ -67,22 +81,38 @@ class ExamplePage extends React.Component {
selected: "",
schema: {},
model: {},
form: []
form: [],
showErrors: false
});
}

fetch(value)
.then(x => x.json())
.then(({ form, schema, model }) => {
this.setState({
schemaJson: JSON.stringify(schema, undefined, 2),
formJson: JSON.stringify(form, undefined, 2),
selected: value,
schema,
model: model || {},
form
});
if (!value.endsWith("json")) {
const elem = examples[value];
this.setState({
schemaJson: JSON.stringify(elem.schema, undefined, 2),
formJson: JSON.stringify(elem.form, undefined, 2),
selected: value,
schema: elem.schema,
model: elem.model || {},
form: elem.form,
localization: elem.localization,
showErrors: false
});
} else {
fetch(value)
.then(x => x.json())
.then(({ form, schema, model }) => {
this.setState({
schemaJson: JSON.stringify(schema, undefined, 2),
formJson: JSON.stringify(form, undefined, 2),
selected: value,
schema,
model: model || {},
form,
showErrors: false
});
});
}
};

onModelChange = (key, val, type) => {
Expand All @@ -95,7 +125,7 @@ class ExamplePage extends React.Component {
onValidate = () => {
const { schema, model } = this.state;
const result = utils.validateBySchema(schema, model);
this.setState({ validationResult: result });
this.setState({ validationResult: result, showErrors: true });
};

onFormChange = val => {
Expand Down Expand Up @@ -125,7 +155,9 @@ class ExamplePage extends React.Component {
selected,
tests,
formJson,
schemaJson
schemaJson,
localization,
showErrors
} = this.state;
const mapper = {
// 'rc-select': RcSelect
Expand All @@ -142,6 +174,8 @@ class ExamplePage extends React.Component {
onModelChange={this.onModelChange}
mapper={mapper}
model={model}
localization={localization}
showErrors={showErrors}
/>
</ErrorBoundary>
);
Expand Down
21 changes: 21 additions & 0 deletions example/data/tests/localizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
form: ["firstName", "date"],
schema: {
type: "object",
title: "Title",
properties: {
firstName: {
title: "first.name",
type: "string"
},
date: {
title: "first.name",
type: "date"
}
}
},
localization: {
getLocalizedString: value =>
value === "first.name" ? "First Name" : value
}
};
18 changes: 15 additions & 3 deletions src/Array.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Typography from "@material-ui/core/Typography";
import cloneDeep from "lodash/cloneDeep";
import utils from "./utils";
import ComposedComponent from "./ComposedComponent";
import type { Localization } from "./types";

const styles = theme => ({
arrayItem: {
Expand All @@ -36,7 +37,8 @@ type Props = {
mapper: any,
options: any,
onChangeValidate: any,
onChange: any
onChange: any,
localization: Localization
};

type State = {
Expand Down Expand Up @@ -171,7 +173,15 @@ class Array extends Component<Props, State> {
};

render() {
const { classes, form, builder, model, mapper, onChange } = this.props;
const {
classes,
form,
builder,
model,
mapper,
onChange,
localization: { getLocalizedString }
} = this.props;
const { model: stateModel } = this.state;
const arrays = [];
for (let i = 0; i < stateModel.length; i += 1) {
Expand All @@ -198,7 +208,9 @@ class Array extends Component<Props, State> {
return (
<div>
<div>
<Typography variant="h6">{form.title}</Typography>
<Typography variant="h6">
{getLocalizedString(form.title)}
</Typography>
<div>{arrays}</div>
</div>
<Button
Expand Down
53 changes: 25 additions & 28 deletions src/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,41 @@
/**
* Created by steve on 20/09/15.
*/
import React, { Component } from "react";
import React from "react";
import Checkbox from "@material-ui/core/Checkbox";
import FormGroup from "@material-ui/core/FormGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import ComposedComponent from "./ComposedComponent";
import type { Localization } from "./types";

type Props = {
onChangeValidate: any,
form: any,
value: any
value: any,
localization: Localization
};

class FormCheckbox extends Component<Props> {
handleChange = e => {
const { onChangeValidate } = this.props;
onChangeValidate(e);
};

render() {
const { form, value } = this.props;
return (
<FormGroup row>
<FormControlLabel
className={form.className}
label={form.title}
control={
<Checkbox
name={form.key.slice(-1)[0]}
value={form.key.slice(-1)[0]}
checked={value || false}
disabled={form.readonly}
onChange={e => this.handleChange(e)}
/>
}
const FormCheckbox = ({
form,
value,
localization: { getLocalizedString },
onChangeValidate
}: Props) => (
<FormGroup row>
<FormControlLabel
className={form.className}
label={getLocalizedString(form.title)}
control={
<Checkbox
name={form.key.slice(-1)[0]}
value={form.key.slice(-1)[0]}
checked={value || false}
disabled={form.readonly}
onChange={onChangeValidate}
/>
</FormGroup>
);
}
}
}
/>
</FormGroup>
);

export default ComposedComponent(FormCheckbox);
34 changes: 25 additions & 9 deletions src/ComposedComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,39 @@ export default (ComposedComponent, defaultProps = {}) =>
class Composed extends React.Component {
constructor(props) {
super(props);
const { errorText, form } = this.props;
const { errorText, form, showErrors } = this.props;
this.onChangeValidate = this.onChangeValidate.bind(this);
const value = defaultValue(this.props);
const validationResult = utils.validate(form, value);
this.state = {
value,
valid: !!(validationResult.valid || !value),
error:
(!validationResult.valid &&
(value ? validationResult.error.message : null)) ||
errorText
};
if (!showErrors) {
this.state = {
value,
valid: true,
error: ""
};
} else {
this.state = {
value,
valid: !!(validationResult.valid || !value),
error:
(!validationResult.valid &&
(value ? validationResult.error.message : null)) ||
errorText
};
}
}

static getDerivedStateFromProps(nextProps) {
const value = defaultValue(nextProps);
const { showErrors } = nextProps;
const validationResult = utils.validate(nextProps.form, value);
if (!showErrors) {
return {
value,
valid: true,
error: ""
};
}
return {
value,
valid: validationResult.valid,
Expand Down
11 changes: 8 additions & 3 deletions src/FieldSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React from "react";
import FormControl from "@material-ui/core/FormControl";
import FormLabel from "@material-ui/core/FormLabel";
import { withStyles } from "@material-ui/core/styles";
import type { Localization } from "./types";

const styles = theme => ({
root: {
Expand All @@ -22,7 +23,8 @@ type Props = {
builder: any,
model: any,
onChange: any,
classes: any
classes: any,
localization: Localization
};

const FieldSet = ({
Expand All @@ -31,15 +33,18 @@ const FieldSet = ({
builder,
model,
onChange,
classes
classes,
localization: { getLocalizedString }
}: Props) => {
const forms = form.items.map((f, index) =>
builder(f, model, index, mapper, onChange, builder)
);

return (
<FormControl component="fieldset" className={classes.root}>
<FormLabel component="legend">{form.title}</FormLabel>
<FormLabel component="legend">
{getLocalizedString(form.title)}
</FormLabel>
<div className={classes.fields}>{forms}</div>
</FormControl>
);
Expand Down
Loading