Skip to content

Commit

Permalink
Expand anyOf support to also support oneOf (#1133)
Browse files Browse the repository at this point in the history
Signed-off-by: Lucian <lucian.buzzo@gmail.com>
  • Loading branch information
LucianBuzzo authored and glasserc committed Jan 18, 2019
1 parent f72be02 commit b068705
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 31 deletions.
16 changes: 3 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1792,8 +1792,6 @@ The JSON Schema standard says that the dependency is triggered if the property i
In this example the user is prompted with different follow-up questions dynamically based on their answer to the first question.
Note that this is quite far from complete `oneOf` support!
In these examples, the "Do you have any pets?" question is validated against the corresponding property in each schema in the `oneOf` array. If exactly one matches, the rest of that schema is merged with the existing schema.
## JSON Schema supporting status
Expand All @@ -1803,13 +1801,9 @@ This component follows [JSON Schema](http://json-schema.org/documentation.html)
* `additionalItems` keyword for arrays
This keyword works when `items` is an array. `additionalItems: true` is not supported because there's no widget to represent an item of any type. In this case it will be treated as no additional items allowed. `additionalItems` being a valid schema is supported.
* `anyOf`, `allOf`, and `oneOf`, or multiple `types` (i.e. `"type": ["string", "array"]`
The `anyOf` keyword is supported but has the following caveats:
- The `anyOf` keyword is not supported when used inside the `items` keyword
for arrays.
- Properties declared inside the `anyOf` should not overlap with properties
"outside" of the `anyOf`.
The `anyOf` and `oneOf` keywords are supported, however, properties declared inside the `anyOf/oneOf` should not overlap with properties "outside" of the `anyOf/oneOf`.
You can use `oneOf` with [schema dependencies](#schema-dependencies) to dynamically add schema properties based on input data but this feature does not bring general support for `oneOf` elsewhere in a schema.
You can also use `oneOf` with [schema dependencies](#schema-dependencies) to dynamically add schema properties based on input data.
## Tips and tricks
Expand Down Expand Up @@ -1881,11 +1875,7 @@ $ git push --tags origin master
### Q: Does rjsf support `oneOf`, `anyOf`, multiple types in an array, etc.?
A: The `anyOf` keyword is supported but has the following caveats:
- The `anyOf` keyword is not supported when used inside the `items` keyword
for arrays.
- Properties declared inside the `anyOf` should not overlap with properties
"outside" of the `anyOf`.
A: The `anyOf` and `oneOf` keywords are supported, however, properties declared inside the `anyOf/oneOf` should not overlap with properties "outside" of the `anyOf/oneOf`.
There is also special cased where you can use `oneOf` in [schema dependencies](#schema-dependencies), If you'd like to help improve support for these keywords, see the following issues for inspiration [#329](https://github.com/mozilla-services/react-jsonschema-form/pull/329) or [#417](https://github.com/mozilla-services/react-jsonschema-form/pull/417). See also: [#52](https://github.com/mozilla-services/react-jsonschema-form/issues/52), [#151](https://github.com/mozilla-services/react-jsonschema-form/issues/151), [#171](https://github.com/mozilla-services/react-jsonschema-form/issues/171), [#200](https://github.com/mozilla-services/react-jsonschema-form/issues/200), [#282](https://github.com/mozilla-services/react-jsonschema-form/issues/282), [#302](https://github.com/mozilla-services/react-jsonschema-form/pull/302), [#330](https://github.com/mozilla-services/react-jsonschema-form/issues/330), [#430](https://github.com/mozilla-services/react-jsonschema-form/issues/430), [#522](https://github.com/mozilla-services/react-jsonschema-form/issues/522), [#538](https://github.com/mozilla-services/react-jsonschema-form/issues/538), [#551](https://github.com/mozilla-services/react-jsonschema-form/issues/551), [#552](https://github.com/mozilla-services/react-jsonschema-form/issues/552), or [#648](https://github.com/mozilla-services/react-jsonschema-form/issues/648).
### Q: Will react-jsonschema-form support Material, Ant-Design, Foundation, or [some other specific widget library or frontend style]?
Expand Down
4 changes: 3 additions & 1 deletion playground/samples/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import arrays from "./arrays";
import anyOf from "./anyOf";
import oneOf from "./oneOf";
import nested from "./nested";
import numbers from "./numbers";
import simple from "./simple";
Expand Down Expand Up @@ -41,5 +42,6 @@ export const samples = {
"Property dependencies": propertyDependencies,
"Schema dependencies": schemaDependencies,
"Additional Properties": additionalProperties,
"Optional Forms": anyOf,
"Any Of": anyOf,
"One Of": oneOf,
};
24 changes: 24 additions & 0 deletions playground/samples/oneOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module.exports = {
schema: {
type: "object",
oneOf: [
{
properties: {
lorem: {
type: "string",
},
},
required: ["lorem"],
},
{
properties: {
ipsum: {
type: "string",
},
},
required: ["ipsum"],
},
],
},
formData: {},
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ class AnyOfField extends Component {
constructor(props) {
super(props);

const { formData, schema } = this.props;
const { formData, options } = this.props;

this.state = {
selectedOption: this.getMatchingOption(formData, schema.anyOf),
selectedOption: this.getMatchingOption(formData, options),
};
}

componentWillReceiveProps(nextProps) {
const matchingOption = this.getMatchingOption(
nextProps.formData,
nextProps.schema.anyOf
nextProps.options
);

if (matchingOption === this.state.selectedOption) {
Expand All @@ -41,8 +41,7 @@ class AnyOfField extends Component {

onOptionChange = event => {
const selectedOption = parseInt(event.target.value, 10);
const { formData, onChange, schema } = this.props;
const options = schema.anyOf;
const { formData, onChange, options } = this.props;

if (guessType(formData) === "object") {
const newFormData = Object.assign({}, formData);
Expand Down Expand Up @@ -73,6 +72,7 @@ class AnyOfField extends Component {

render() {
const {
baseType,
disabled,
errorSchema,
formData,
Expand All @@ -81,7 +81,7 @@ class AnyOfField extends Component {
onBlur,
onChange,
onFocus,
schema,
options,
registry,
safeRenderCompletion,
uiSchema,
Expand All @@ -90,8 +90,6 @@ class AnyOfField extends Component {
const _SchemaField = registry.fields.SchemaField;
const { selectedOption } = this.state;

const baseType = schema.type;
const options = schema.anyOf || [];
const option = options[selectedOption] || null;
let optionSchema;

Expand Down Expand Up @@ -151,7 +149,8 @@ AnyOfField.defaultProps = {

if (process.env.NODE_ENV !== "production") {
AnyOfField.propTypes = {
schema: PropTypes.object.isRequired,
options: PropTypes.arrayOf(PropTypes.object).isRequired,
baseType: PropTypes.string,
uiSchema: PropTypes.object,
idSchema: PropTypes.object,
formData: PropTypes.any,
Expand Down
32 changes: 26 additions & 6 deletions src/components/fields/SchemaField.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ function getFieldComponent(schema, uiSchema, idSchema, fields) {

const componentName = COMPONENT_TYPES[getSchemaType(schema)];

// If the type is not defined and the schema uses 'anyOf', don't render
// a field and let the AnyOfField component handle the form display
if (!componentName && schema.anyOf) {
// If the type is not defined and the schema uses 'anyOf' or 'oneOf', don't
// render a field and let the MultiSchemaField component handle the form display
if (!componentName && (schema.anyOf || schema.oneOf)) {
return () => null;
}

Expand Down Expand Up @@ -330,14 +330,15 @@ function SchemaFieldRender(props) {
};

const _AnyOfField = registry.fields.AnyOfField;
const _OneOfField = registry.fields.OneOfField;

return (
<FieldTemplate {...fieldProps}>
{field}

{/*
If the schema `anyOf` can be rendered as a select control, don't
render the `anyOf` selection and let `StringField` component handle
If the schema `anyOf` or 'oneOf' can be rendered as a select control, don't
render the selection and let `StringField` component handle
rendering
*/}
{schema.anyOf && !isSelect(schema) && (
Expand All @@ -350,7 +351,26 @@ function SchemaFieldRender(props) {
onBlur={props.onBlur}
onChange={props.onChange}
onFocus={props.onFocus}
schema={schema}
options={schema.anyOf}
baseType={schema.type}
registry={registry}
safeRenderCompletion={props.safeRenderCompletion}
uiSchema={uiSchema}
/>
)}

{schema.oneOf && !isSelect(schema) && (
<_OneOfField
disabled={disabled}
errorSchema={errorSchema}
formData={formData}
idPrefix={idPrefix}
idSchema={idSchema}
onBlur={props.onBlur}
onChange={props.onChange}
onFocus={props.onFocus}
options={schema.oneOf}
baseType={schema.type}
registry={registry}
safeRenderCompletion={props.safeRenderCompletion}
uiSchema={uiSchema}
Expand Down
5 changes: 3 additions & 2 deletions src/components/fields/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import AnyOfField from "./AnyOfField";
import ArrayField from "./ArrayField";
import BooleanField from "./BooleanField";
import DescriptionField from "./DescriptionField";
import MultiSchemaField from "./MultiSchemaField";
import NumberField from "./NumberField";
import ObjectField from "./ObjectField";
import SchemaField from "./SchemaField";
Expand All @@ -10,12 +10,13 @@ import TitleField from "./TitleField";
import UnsupportedField from "./UnsupportedField";

export default {
AnyOfField,
AnyOfField: MultiSchemaField,
ArrayField,
BooleanField,
DescriptionField,
NumberField,
ObjectField,
OneOfField: MultiSchemaField,
SchemaField,
StringField,
TitleField,
Expand Down
Loading

0 comments on commit b068705

Please sign in to comment.