├── .npmignore
├── test
├── mocha.opts
├── .eslintrc
├── setup-jsdom.js
├── test_utils.js
├── const_test.js
├── NullField_test.js
├── DescriptionField_test.js
├── TitleField_test.js
├── FieldTemplate_test.js
├── ObjectFieldTemplate_test.js
├── FormContext_test.js
├── performance_test.js
├── withTheme_test.js
└── ArrayFieldTemplate_test.js
├── netlify.toml
├── .editorconfig
├── src
├── index.js
├── components
│ ├── IconButton.js
│ ├── widgets
│ │ ├── URLWidget.js
│ │ ├── EmailWidget.js
│ │ ├── PasswordWidget.js
│ │ ├── TextWidget.js
│ │ ├── DateWidget.js
│ │ ├── UpDownWidget.js
│ │ ├── HiddenWidget.js
│ │ ├── RangeWidget.js
│ │ ├── AltDateTimeWidget.js
│ │ ├── ColorWidget.js
│ │ ├── index.js
│ │ ├── TextareaWidget.js
│ │ ├── DateTimeWidget.js
│ │ ├── RadioWidget.js
│ │ ├── CheckboxWidget.js
│ │ ├── BaseInput.js
│ │ ├── CheckboxesWidget.js
│ │ ├── FileWidget.js
│ │ ├── SelectWidget.js
│ │ └── AltDateWidget.js
│ ├── fields
│ │ ├── NullField.js
│ │ ├── TitleField.js
│ │ ├── DescriptionField.js
│ │ ├── index.js
│ │ ├── UnsupportedField.js
│ │ ├── StringField.js
│ │ ├── BooleanField.js
│ │ ├── NumberField.js
│ │ ├── MultiSchemaField.js
│ │ └── ObjectField.js
│ ├── AddButton.js
│ └── ErrorList.js
├── withTheme.js
├── types.js
└── validate.js
├── playground
├── samples
│ ├── single.js
│ ├── custom.js
│ ├── oneOf.js
│ ├── files.js
│ ├── null.js
│ ├── additionalProperties.js
│ ├── errors.js
│ ├── ordering.js
│ ├── large.js
│ ├── validation.js
│ ├── anyOf.js
│ ├── date.js
│ ├── numbers.js
│ ├── references.js
│ ├── simple.js
│ ├── customArray.js
│ ├── index.js
│ ├── customObject.js
│ ├── nested.js
│ ├── nullable.js
│ ├── alternatives.js
│ ├── propertyDependencies.js
│ ├── arrays.js
│ ├── widgets.js
│ └── schemaDependencies.js
├── index.html
└── index.prod.html
├── README.md
├── .travis.yml
├── .babelrc
├── mkdocs.yml
├── PULL_REQUEST_TEMPLATE.md
├── CODE_OF_CONDUCT.md
├── types
├── README.md
├── LICENSE
└── index.d.ts
├── ISSUE_TEMPLATE.md
├── devServer.js
├── .eslintrc
├── webpack.config.dev.js
├── docs
├── definitions.md
├── theme-customization.md
├── dependencies.md
└── validation.md
├── webpack.config.dist.js
├── webpack.config.prod.js
├── .gitignore
└── package.json
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --timeout 5000
2 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [context.deploy-preview.environment]
2 | SHOW_NETLIFY_BADGE = "true"
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 |
3 | indent_style = space
4 | indent_size = 2
5 | charset = utf-8
6 | insert_final_newline = true
7 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Form from "./components/Form";
2 | import withTheme from "./withTheme";
3 |
4 | export { withTheme };
5 | export default Form;
6 |
--------------------------------------------------------------------------------
/playground/samples/single.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "A single-field form",
4 | type: "string",
5 | },
6 | formData: "initial value",
7 | uiSchema: {},
8 | };
9 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true,
4 | },
5 | "globals": {
6 | d: true
7 | },
8 | "rules": {
9 | "no-unused-vars": [
10 | 2,
11 | {
12 | "varsIgnorePattern": "^d$"
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DEPRECATION NOTICE
2 | ==================
3 |
4 | This project is no longer maintained. Current versions of [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form) now include a Bootstrap 4 theme. It is recommended that you switch to that instead.
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language:
3 | - node_js
4 | node_js:
5 | - "6"
6 | - "7"
7 | - "8"
8 | - "9"
9 | - "10"
10 | - "11"
11 | - "12"
12 | env:
13 | - ACTION=test
14 | - ACTION="run lint"
15 | - ACTION="run cs-check"
16 | - ACTION="run dist"
17 | script:
18 | - npm $ACTION
19 |
--------------------------------------------------------------------------------
/src/components/IconButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function IconButton(props) {
4 | const { type = "default", icon, className, ...otherProps } = props;
5 | return (
6 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/widgets/URLWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function URLWidget(props) {
5 | const { BaseInput } = props.registry.widgets;
6 | return ;
7 | }
8 |
9 | if (process.env.NODE_ENV !== "production") {
10 | URLWidget.propTypes = {
11 | value: PropTypes.string,
12 | };
13 | }
14 |
15 | export default URLWidget;
16 |
--------------------------------------------------------------------------------
/src/components/widgets/EmailWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function EmailWidget(props) {
5 | const { BaseInput } = props.registry.widgets;
6 | return ;
7 | }
8 |
9 | if (process.env.NODE_ENV !== "production") {
10 | EmailWidget.propTypes = {
11 | value: PropTypes.string,
12 | };
13 | }
14 |
15 | export default EmailWidget;
16 |
--------------------------------------------------------------------------------
/src/components/widgets/PasswordWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function PasswordWidget(props) {
5 | const { BaseInput } = props.registry.widgets;
6 | return ;
7 | }
8 |
9 | if (process.env.NODE_ENV !== "production") {
10 | PasswordWidget.propTypes = {
11 | value: PropTypes.string,
12 | };
13 | }
14 |
15 | export default PasswordWidget;
16 |
--------------------------------------------------------------------------------
/playground/samples/custom.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "A localisation form",
4 | type: "object",
5 | required: ["lat", "lon"],
6 | properties: {
7 | lat: {
8 | type: "number",
9 | },
10 | lon: {
11 | type: "number",
12 | },
13 | },
14 | },
15 | uiSchema: {
16 | "ui:field": "geo",
17 | },
18 | formData: {
19 | lat: 0,
20 | lon: 0,
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/widgets/TextWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function TextWidget(props) {
5 | const { BaseInput } = props.registry.widgets;
6 | return ;
7 | }
8 |
9 | if (process.env.NODE_ENV !== "production") {
10 | TextWidget.propTypes = {
11 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
12 | id: PropTypes.string,
13 | };
14 | }
15 |
16 | export default TextWidget;
17 |
--------------------------------------------------------------------------------
/src/components/fields/NullField.js:
--------------------------------------------------------------------------------
1 | import { Component } from "react";
2 | import * as types from "../../types";
3 |
4 | class NullField extends Component {
5 | componentDidMount() {
6 | if (this.props.formData === undefined) {
7 | this.props.onChange(null);
8 | }
9 | }
10 |
11 | render() {
12 | return null;
13 | }
14 | }
15 |
16 | if (process.env.NODE_ENV !== "production") {
17 | NullField.propTypes = types.fieldProps;
18 | }
19 |
20 | export default NullField;
21 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react",
4 | "@babel/preset-env"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-proposal-object-rest-spread",
8 | "@babel/plugin-proposal-class-properties",
9 | [
10 | "@babel/plugin-transform-runtime",
11 | {
12 | "corejs": 2
13 | }
14 | ]
15 | ],
16 | "env": {
17 | "development": {
18 | "plugins": [
19 | "@babel/plugin-transform-react-jsx"
20 | ]
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/playground/samples/oneOf.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | type: "object",
4 | oneOf: [
5 | {
6 | properties: {
7 | lorem: {
8 | type: "string",
9 | },
10 | },
11 | required: ["lorem"],
12 | },
13 | {
14 | properties: {
15 | ipsum: {
16 | type: "string",
17 | },
18 | },
19 | required: ["ipsum"],
20 | },
21 | ],
22 | },
23 | formData: {},
24 | };
25 |
--------------------------------------------------------------------------------
/playground/samples/files.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "Files",
4 | type: "object",
5 | properties: {
6 | file: {
7 | type: "string",
8 | format: "data-url",
9 | title: "Single file",
10 | },
11 | files: {
12 | type: "array",
13 | title: "Multiple files",
14 | items: {
15 | type: "string",
16 | format: "data-url",
17 | },
18 | },
19 | },
20 | },
21 | uiSchema: {},
22 | formData: {},
23 | };
24 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: react-jsonschema-form documentation
2 | docs_dir: docs
3 | theme: readthedocs
4 |
5 | nav:
6 | - Introduction: index.md
7 | - Advanced Customization: advanced-customization.md
8 | - Definitions: definitions.md
9 | - Dependencies: dependencies.md
10 | - Form Customization: form-customization.md
11 | - Theme Customization: theme-customization.md
12 | - Validation: validation.md
13 | - Playground: https://mozilla-services.github.io/react-jsonschema-form/
14 |
15 | markdown_extensions:
16 | - toc:
17 | permalink: true
18 |
--------------------------------------------------------------------------------
/src/components/AddButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import IconButton from "./IconButton";
3 |
4 | export default function AddButton({ className, onClick, disabled }) {
5 | return (
6 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/ErrorList.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function ErrorList(props) {
4 | const { errors } = props;
5 | return (
6 |
7 |
Errors
8 |
9 | {errors.map((error, i) => {
10 | return (
11 | -
12 | {error.stack}
13 |
14 | );
15 | })}
16 |
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/widgets/DateWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function DateWidget(props) {
5 | const {
6 | onChange,
7 | registry: {
8 | widgets: { BaseInput },
9 | },
10 | } = props;
11 | return (
12 | onChange(value || undefined)}
16 | />
17 | );
18 | }
19 |
20 | if (process.env.NODE_ENV !== "production") {
21 | DateWidget.propTypes = {
22 | value: PropTypes.string,
23 | };
24 | }
25 |
26 | export default DateWidget;
27 |
--------------------------------------------------------------------------------
/src/components/widgets/UpDownWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { rangeSpec } from "../../utils";
5 |
6 | function UpDownWidget(props) {
7 | const {
8 | registry: {
9 | widgets: { BaseInput },
10 | },
11 | } = props;
12 | return ;
13 | }
14 |
15 | if (process.env.NODE_ENV !== "production") {
16 | UpDownWidget.propTypes = {
17 | value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
18 | };
19 | }
20 |
21 | export default UpDownWidget;
22 |
--------------------------------------------------------------------------------
/test/setup-jsdom.js:
--------------------------------------------------------------------------------
1 | var jsdom = require("jsdom");
2 |
3 | // Setup the jsdom environment
4 | // @see https://github.com/facebook/react/issues/5046
5 | if (!global.hasOwnProperty("window")) {
6 | global.document = jsdom.jsdom("");
7 | global.window = document.defaultView;
8 | global.navigator = global.window.navigator;
9 | global.File = global.window.File;
10 | }
11 |
12 | // atob
13 | global.atob = require("atob");
14 |
15 | // HTML debugging helper
16 | global.d = function d(node) {
17 | console.log(require("html").prettyPrint(node.outerHTML, { indent_size: 2 }));
18 | };
19 |
--------------------------------------------------------------------------------
/src/components/widgets/HiddenWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function HiddenWidget({ id, value }) {
5 | return (
6 |
11 | );
12 | }
13 |
14 | if (process.env.NODE_ENV !== "production") {
15 | HiddenWidget.propTypes = {
16 | id: PropTypes.string.isRequired,
17 | value: PropTypes.oneOfType([
18 | PropTypes.string,
19 | PropTypes.number,
20 | PropTypes.bool,
21 | ]),
22 | };
23 | }
24 |
25 | export default HiddenWidget;
26 |
--------------------------------------------------------------------------------
/src/components/fields/TitleField.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const REQUIRED_FIELD_SYMBOL = "*";
5 |
6 | function TitleField(props) {
7 | const { id, title, required } = props;
8 | return (
9 |
13 | );
14 | }
15 |
16 | if (process.env.NODE_ENV !== "production") {
17 | TitleField.propTypes = {
18 | id: PropTypes.string,
19 | title: PropTypes.string,
20 | required: PropTypes.bool,
21 | };
22 | }
23 |
24 | export default TitleField;
25 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Reasons for making this change
2 |
3 | [Please describe them here]
4 |
5 | If this is related to existing tickets, include links to them as well.
6 |
7 | ### Checklist
8 |
9 | * [ ] **I'm updating documentation**
10 | - [ ] I've [checked the rendering](https://react-jsonschema-form.readthedocs.io/en/latest/#contributing) of the Markdown text I've added
11 | * [ ] **I'm adding or updating code**
12 | - [ ] I've added and/or updated tests
13 | - [ ] I've updated [docs](https://react-jsonschema-form.readthedocs.io/) if needed
14 | * [ ] **I'm adding a new feature**
15 | - [ ] I've updated the playground with an example use of the feature
16 |
--------------------------------------------------------------------------------
/playground/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | react-jsonschema-form playground
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/playground/index.prod.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | react-jsonschema-form playground
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/playground/samples/null.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "Null field example",
4 | description: "A short form with a null field",
5 | type: "object",
6 | required: ["firstName"],
7 | properties: {
8 | helpText: {
9 | title: "A null field",
10 | description:
11 | "Null fields like this are great for adding extra information",
12 | type: "null",
13 | },
14 | firstName: {
15 | type: "string",
16 | title: "A regular string field",
17 | default: "Chuck",
18 | },
19 | },
20 | },
21 | uiSchema: {
22 | firstName: {
23 | "ui:autofocus": true,
24 | "ui:emptyValue": "",
25 | },
26 | },
27 | formData: {},
28 | };
29 |
--------------------------------------------------------------------------------
/src/components/widgets/RangeWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { rangeSpec } from "../../utils";
5 |
6 | function RangeWidget(props) {
7 | const {
8 | schema,
9 | value,
10 | registry: {
11 | widgets: { BaseInput },
12 | },
13 | } = props;
14 | return (
15 |
16 |
17 | {value}
18 |
19 | );
20 | }
21 |
22 | if (process.env.NODE_ENV !== "production") {
23 | RangeWidget.propTypes = {
24 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
25 | };
26 | }
27 |
28 | export default RangeWidget;
29 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Community Participation Guidelines
2 |
3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines.
4 | For more details, please read the
5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
6 |
7 | ## How to Report
8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
9 |
10 |
16 |
--------------------------------------------------------------------------------
/src/withTheme.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import Form from "./";
4 |
5 | function withTheme(themeProps) {
6 | return class extends Component {
7 | render() {
8 | let { fields, widgets, ...directProps } = this.props;
9 | fields = { ...themeProps.fields, ...fields };
10 | widgets = { ...themeProps.widgets, ...widgets };
11 | return (
12 |
18 | );
19 | }
20 | };
21 | }
22 |
23 | withTheme.propTypes = {
24 | widgets: PropTypes.object,
25 | fields: PropTypes.object,
26 | };
27 |
28 | export default withTheme;
29 |
--------------------------------------------------------------------------------
/src/components/widgets/AltDateTimeWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import AltDateWidget from "./AltDateWidget";
4 |
5 | function AltDateTimeWidget(props) {
6 | const { AltDateWidget } = props.registry.widgets;
7 | return ;
8 | }
9 |
10 | if (process.env.NODE_ENV !== "production") {
11 | AltDateTimeWidget.propTypes = {
12 | schema: PropTypes.object.isRequired,
13 | id: PropTypes.string.isRequired,
14 | value: PropTypes.string,
15 | required: PropTypes.bool,
16 | onChange: PropTypes.func,
17 | options: PropTypes.object,
18 | };
19 | }
20 |
21 | AltDateTimeWidget.defaultProps = {
22 | ...AltDateWidget.defaultProps,
23 | time: true,
24 | };
25 |
26 | export default AltDateTimeWidget;
27 |
--------------------------------------------------------------------------------
/src/components/widgets/ColorWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function ColorWidget(props) {
5 | const {
6 | disabled,
7 | readonly,
8 | registry: {
9 | widgets: { BaseInput },
10 | },
11 | } = props;
12 | return ;
13 | }
14 |
15 | if (process.env.NODE_ENV !== "production") {
16 | ColorWidget.propTypes = {
17 | schema: PropTypes.object.isRequired,
18 | id: PropTypes.string.isRequired,
19 | value: PropTypes.string,
20 | required: PropTypes.bool,
21 | disabled: PropTypes.bool,
22 | readonly: PropTypes.bool,
23 | autofocus: PropTypes.bool,
24 | onChange: PropTypes.func,
25 | };
26 | }
27 |
28 | export default ColorWidget;
29 |
--------------------------------------------------------------------------------
/playground/samples/additionalProperties.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "A customizable registration form",
4 | description: "A simple form with additional properties example.",
5 | type: "object",
6 | required: ["firstName", "lastName"],
7 | additionalProperties: {
8 | type: "string",
9 | },
10 | properties: {
11 | firstName: {
12 | type: "string",
13 | title: "First name",
14 | },
15 | lastName: {
16 | type: "string",
17 | title: "Last name",
18 | },
19 | },
20 | },
21 | uiSchema: {
22 | firstName: {
23 | "ui:autofocus": true,
24 | "ui:emptyValue": "",
25 | },
26 | },
27 | formData: {
28 | firstName: "Chuck",
29 | lastName: "Norris",
30 | assKickCount: "infinity",
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/fields/DescriptionField.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function DescriptionField(props) {
5 | const { id, description } = props;
6 | if (!description) {
7 | return null;
8 | }
9 | if (typeof description === "string") {
10 | return (
11 |
12 | {description}
13 |
14 | );
15 | } else {
16 | return (
17 |
18 | {description}
19 |
20 | );
21 | }
22 | }
23 |
24 | if (process.env.NODE_ENV !== "production") {
25 | DescriptionField.propTypes = {
26 | id: PropTypes.string,
27 | description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
28 | };
29 | }
30 |
31 | export default DescriptionField;
32 |
--------------------------------------------------------------------------------
/src/components/fields/index.js:
--------------------------------------------------------------------------------
1 | import ArrayField from "./ArrayField";
2 | import BooleanField from "./BooleanField";
3 | import DescriptionField from "./DescriptionField";
4 | import MultiSchemaField from "./MultiSchemaField";
5 | import NumberField from "./NumberField";
6 | import ObjectField from "./ObjectField";
7 | import SchemaField from "./SchemaField";
8 | import StringField from "./StringField";
9 | import TitleField from "./TitleField";
10 | import NullField from "./NullField";
11 | import UnsupportedField from "./UnsupportedField";
12 |
13 | export default {
14 | AnyOfField: MultiSchemaField,
15 | ArrayField,
16 | BooleanField,
17 | DescriptionField,
18 | NumberField,
19 | ObjectField,
20 | OneOfField: MultiSchemaField,
21 | SchemaField,
22 | StringField,
23 | TitleField,
24 | NullField,
25 | UnsupportedField,
26 | };
27 |
--------------------------------------------------------------------------------
/types/README.md:
--------------------------------------------------------------------------------
1 | # Installation
2 | > `npm install --save @types/react-jsonschema-form`
3 |
4 | # Summary
5 | This package contains type definitions for react-jsonschema-form (https://github.com/mozilla-services/react-jsonschema-form).
6 |
7 | # Details
8 | Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-jsonschema-form
9 |
10 | Additional Details
11 | * Last updated: Thu, 29 Nov 2018 23:46:25 GMT
12 | * Dependencies: react, json-schema
13 | * Global values: none
14 |
15 | # Credits
16 | These definitions were written by Dan Fox , Ivan Jiang , Philippe Bourdages , Lucian Buzzo , Sylvain Thénault , Sebastian Busch .
17 |
--------------------------------------------------------------------------------
/src/components/fields/UnsupportedField.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function UnsupportedField({ schema, idSchema, reason }) {
5 | return (
6 |
7 |
8 | Unsupported field schema
9 | {idSchema && idSchema.$id && (
10 |
11 | {" for"} field {idSchema.$id}
12 |
13 | )}
14 | {reason && : {reason}}.
15 |
16 | {schema &&
{JSON.stringify(schema, null, 2)}}
17 |
18 | );
19 | }
20 |
21 | if (process.env.NODE_ENV !== "production") {
22 | UnsupportedField.propTypes = {
23 | schema: PropTypes.object.isRequired,
24 | idSchema: PropTypes.object,
25 | reason: PropTypes.string,
26 | };
27 | }
28 |
29 | export default UnsupportedField;
30 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Prerequisites
2 |
3 | - [ ] I have read the [documentation](https://react-jsonschema-form.readthedocs.io/);
4 | - [ ] In the case of a bug report, I understand that providing a [SSCCE](http://sscce.org/) example is tremendously useful to the maintainers.
5 | - [ ] Ideally, I'm providing a [sample JSFiddle](https://jsfiddle.net/n1k0/f2y3fq7L/6/) or a [shared playground link](https://mozilla-services.github.io/react-jsonschema-form/) demonstrating the issue.
6 |
7 | ### Description
8 |
9 | [Description of the bug or feature]
10 |
11 | ### Steps to Reproduce
12 |
13 | 1. [First Step]
14 | 2. [Second Step]
15 | 3. [and so on...]
16 |
17 | #### Expected behavior
18 |
19 | [What you expected to happen]
20 |
21 | #### Actual behavior
22 |
23 | [What actually happened]
24 |
25 | ### Version
26 |
27 | You can usually get this information in your `package.json` or in the file URL if you're using the unpkg one.
28 |
--------------------------------------------------------------------------------
/test/test_utils.js:
--------------------------------------------------------------------------------
1 | /* Utils for tests. */
2 |
3 | import React from "react";
4 | import sinon from "sinon";
5 | import { renderIntoDocument } from "react-dom/test-utils";
6 | import { findDOMNode, render } from "react-dom";
7 |
8 | import Form from "../src";
9 |
10 | export function createComponent(Component, props) {
11 | const comp = renderIntoDocument();
12 | const node = findDOMNode(comp);
13 | return { comp, node };
14 | }
15 |
16 | export function createFormComponent(props) {
17 | return createComponent(Form, { ...props, safeRenderCompletion: true });
18 | }
19 |
20 | export function createSandbox() {
21 | const sandbox = sinon.sandbox.create();
22 | // Ensure we catch any React warning and mark them as test failures.
23 | sandbox.stub(console, "error", error => {
24 | throw new Error(error);
25 | });
26 | return sandbox;
27 | }
28 |
29 | export function setProps(comp, newProps) {
30 | const node = findDOMNode(comp);
31 | render(React.createElement(comp.constructor, newProps), node.parentNode);
32 | }
33 |
--------------------------------------------------------------------------------
/playground/samples/errors.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "Contextualized errors",
4 | type: "object",
5 | properties: {
6 | firstName: {
7 | type: "string",
8 | title: "First name",
9 | minLength: 8,
10 | pattern: "\\d+",
11 | },
12 | active: {
13 | type: "boolean",
14 | title: "Active",
15 | },
16 | skills: {
17 | type: "array",
18 | items: {
19 | type: "string",
20 | minLength: 5,
21 | },
22 | },
23 | multipleChoicesList: {
24 | type: "array",
25 | title: "Pick max two items",
26 | uniqueItems: true,
27 | maxItems: 2,
28 | items: {
29 | type: "string",
30 | enum: ["foo", "bar", "fuzz"],
31 | },
32 | },
33 | },
34 | },
35 | uiSchema: {},
36 | formData: {
37 | firstName: "Chuck",
38 | active: "wrong",
39 | skills: ["karate", "budo", "aikido"],
40 | multipleChoicesList: ["foo", "bar", "fuzz"],
41 | },
42 | };
43 |
--------------------------------------------------------------------------------
/devServer.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const express = require("express");
3 | const webpack = require("webpack");
4 |
5 | const server = process.env.RJSF_DEV_SERVER || "localhost:8080";
6 | const splitServer = server.split(":");
7 | const host = splitServer[0];
8 | const port = splitServer[1];
9 | const env = "dev";
10 |
11 | const webpackConfig = require("./webpack.config." + env);
12 | const compiler = webpack(webpackConfig);
13 | const app = express();
14 |
15 | app.use(require("webpack-dev-middleware")(compiler, {
16 | publicPath: webpackConfig.output.publicPath,
17 | noInfo: true
18 | }));
19 |
20 | app.use(require("webpack-hot-middleware")(compiler));
21 |
22 | app.get("/favicon.ico", function(req, res) {
23 | res.status(204).end();
24 | });
25 |
26 | app.get("/", function(req, res) {
27 | res.sendFile(path.join(__dirname, "playground", "index.html"));
28 | });
29 |
30 | app.listen(port, host, function(err) {
31 | if (err) {
32 | console.log(err);
33 | return;
34 | }
35 |
36 | console.log(`Listening at http://${server}`);
37 | });
38 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "rules": {
4 | "react/jsx-uses-react": 2,
5 | "react/jsx-uses-vars": 2,
6 | "react/react-in-jsx-scope": 2,
7 | "react/jsx-tag-spacing": [1, {
8 | "beforeSelfClosing": "always"
9 | }],
10 | "curly": [2],
11 | "linebreak-style": [2, "unix"],
12 | "semi": [2, "always"],
13 | "comma-dangle": [0],
14 | "no-unused-vars": [2, {
15 | "vars": "all",
16 | "args": "none",
17 | "ignoreRestSiblings": true
18 | }],
19 | "no-console": [0],
20 | "object-curly-spacing": [2, "always"],
21 | "keyword-spacing": ["error"]
22 | },
23 | "env": {
24 | "es6": true,
25 | "browser": true,
26 | "node": true
27 | },
28 | "extends": "eslint:recommended",
29 | "ecmaFeatures": {
30 | "modules": true,
31 | "jsx": true,
32 | "experimentalObjectRestSpread": true
33 | },
34 | "plugins": [
35 | "jsx-a11y",
36 | "react"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/playground/samples/ordering.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "A registration form",
4 | type: "object",
5 | required: ["firstName", "lastName"],
6 | properties: {
7 | password: {
8 | type: "string",
9 | title: "Password",
10 | },
11 | lastName: {
12 | type: "string",
13 | title: "Last name",
14 | },
15 | bio: {
16 | type: "string",
17 | title: "Bio",
18 | },
19 | firstName: {
20 | type: "string",
21 | title: "First name",
22 | },
23 | age: {
24 | type: "integer",
25 | title: "Age",
26 | },
27 | },
28 | },
29 | uiSchema: {
30 | "ui:order": ["firstName", "lastName", "*", "password"],
31 | age: {
32 | "ui:widget": "updown",
33 | },
34 | bio: {
35 | "ui:widget": "textarea",
36 | },
37 | password: {
38 | "ui:widget": "password",
39 | },
40 | },
41 | formData: {
42 | firstName: "Chuck",
43 | lastName: "Norris",
44 | age: 75,
45 | bio: "Roundhouse kicking asses since 1940",
46 | password: "noneed",
47 | },
48 | };
49 |
--------------------------------------------------------------------------------
/playground/samples/large.js:
--------------------------------------------------------------------------------
1 | function largeEnum(n) {
2 | const list = [];
3 | for (let i = 0; i < n; i++) {
4 | list.push("option #" + i);
5 | }
6 | return list;
7 | }
8 |
9 | module.exports = {
10 | schema: {
11 | definitions: {
12 | largeEnum: { type: "string", enum: largeEnum(100) },
13 | },
14 | title: "A rather large form",
15 | type: "object",
16 | properties: {
17 | string: {
18 | type: "string",
19 | title: "Some string",
20 | },
21 | choice1: { $ref: "#/definitions/largeEnum" },
22 | choice2: { $ref: "#/definitions/largeEnum" },
23 | choice3: { $ref: "#/definitions/largeEnum" },
24 | choice4: { $ref: "#/definitions/largeEnum" },
25 | choice5: { $ref: "#/definitions/largeEnum" },
26 | choice6: { $ref: "#/definitions/largeEnum" },
27 | choice7: { $ref: "#/definitions/largeEnum" },
28 | choice8: { $ref: "#/definitions/largeEnum" },
29 | choice9: { $ref: "#/definitions/largeEnum" },
30 | choice10: { $ref: "#/definitions/largeEnum" },
31 | },
32 | },
33 | uiSchema: {
34 | choice1: {
35 | "ui:placeholder": "Choose one",
36 | },
37 | },
38 | formData: {},
39 | };
40 |
--------------------------------------------------------------------------------
/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require("path");
2 | var webpack = require("webpack");
3 |
4 | module.exports = {
5 | mode: "development",
6 | devtool: "source-map",
7 | entry: [
8 | "webpack-hot-middleware/client?reload=true",
9 | "./playground/app"
10 | ],
11 | output: {
12 | path: path.join(__dirname, "build"),
13 | filename: "bundle.js",
14 | publicPath: "/static/"
15 | },
16 | plugins: [
17 | new webpack.HotModuleReplacementPlugin(),
18 | ],
19 | module: {
20 | rules: [
21 | {
22 | test: /\.jsx?$/,
23 | use: [
24 | "babel-loader",
25 | ],
26 | include: [
27 | path.join(__dirname, "src"),
28 | path.join(__dirname, "playground"),
29 | path.join(__dirname, "node_modules", "codemirror", "mode", "javascript"),
30 | ]
31 | },
32 | {
33 | test: /\.css$/,
34 | use: [
35 | "style-loader",
36 | "css-loader",
37 | ],
38 | include: [
39 | path.join(__dirname, "css"),
40 | path.join(__dirname, "playground"),
41 | path.join(__dirname, "node_modules"),
42 | ],
43 | },
44 | ]
45 | }
46 | };
47 |
--------------------------------------------------------------------------------
/src/types.js:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 |
3 | export const registry = PropTypes.shape({
4 | ArrayFieldTemplate: PropTypes.func,
5 | FieldTemplate: PropTypes.func,
6 | ObjectFieldTemplate: PropTypes.func,
7 | definitions: PropTypes.object.isRequired,
8 | fields: PropTypes.objectOf(PropTypes.func).isRequired,
9 | formContext: PropTypes.object.isRequired,
10 | widgets: PropTypes.objectOf(
11 | PropTypes.oneOfType([PropTypes.func, PropTypes.object])
12 | ).isRequired,
13 | });
14 |
15 | export const fieldProps = {
16 | autofocus: PropTypes.bool,
17 | disabled: PropTypes.bool,
18 | errorSchema: PropTypes.object,
19 | formData: PropTypes.any,
20 | idSchema: PropTypes.object,
21 | onBlur: PropTypes.func,
22 | onChange: PropTypes.func.isRequired,
23 | onFocus: PropTypes.func,
24 | rawErrors: PropTypes.arrayOf(PropTypes.string),
25 | readonly: PropTypes.bool,
26 | registry: registry.isRequired,
27 | required: PropTypes.bool,
28 | schema: PropTypes.object.isRequired,
29 | uiSchema: PropTypes.shape({
30 | "ui:options": PropTypes.shape({
31 | addable: PropTypes.bool,
32 | orderable: PropTypes.bool,
33 | removable: PropTypes.bool,
34 | }),
35 | }),
36 | };
37 |
--------------------------------------------------------------------------------
/docs/definitions.md:
--------------------------------------------------------------------------------
1 | ## Schema definitions and references
2 |
3 | This library partially supports [inline schema definition dereferencing]( http://json-schema.org/latest/json-schema-core.html#rfc.section.7.2.3), which is Barbarian for *avoiding to copy and paste commonly used field schemas*:
4 |
5 | ```json
6 | {
7 | "definitions": {
8 | "address": {
9 | "type": "object",
10 | "properties": {
11 | "street_address": { "type": "string" },
12 | "city": { "type": "string" },
13 | "state": { "type": "string" }
14 | },
15 | "required": ["street_address", "city", "state"]
16 | }
17 | },
18 | "type": "object",
19 | "properties": {
20 | "billing_address": { "$ref": "#/definitions/address" },
21 | "shipping_address": { "$ref": "#/definitions/address" }
22 | }
23 | }
24 | ```
25 |
26 | *(Sample schema courtesy of the [Space Telescope Science Institute](http://spacetelescope.github.io/understanding-json-schema/structuring.html))*
27 |
28 | Note that it only supports local definition referencing; we do not plan on fetching foreign schemas over HTTP anytime soon. Basically, you can only reference a definition from the very schema object defining it.
29 |
30 |
--------------------------------------------------------------------------------
/types/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/webpack.config.dist.js:
--------------------------------------------------------------------------------
1 | var path = require("path");
2 | var webpack = require("webpack");
3 |
4 | module.exports = {
5 | mode: "production",
6 | cache: true,
7 | context: __dirname + "/src",
8 | entry: "./index.js",
9 | output: {
10 | path: path.join(__dirname, "dist"),
11 | publicPath: "/dist/",
12 | filename: "react-jsonschema-form.js",
13 | library: "JSONSchemaForm",
14 | libraryTarget: "umd"
15 | },
16 | plugins: [
17 | new webpack.DefinePlugin({
18 | "process.env": {
19 | NODE_ENV: JSON.stringify("production")
20 | }
21 | })
22 | ],
23 | devtool: "source-map",
24 | externals: {
25 | react: {
26 | root: "React",
27 | commonjs: "react",
28 | commonjs2: "react",
29 | amd: "react"
30 | },
31 | 'react-dom': {
32 | root: "ReactDOM",
33 | commonjs2: 'react-dom',
34 | commonjs: 'react-dom',
35 | amd: 'react-dom',
36 | umd: 'react-dom',
37 | }
38 | },
39 | module: {
40 | rules: [
41 | {
42 | test: /\.js$/,
43 | use: [
44 | "babel-loader",
45 | ],
46 | exclude: [
47 | path.join(__dirname, "node_modules", "core-js"),
48 | path.join(__dirname, "node_modules", "babel-runtime"),
49 | ],
50 | },
51 | ]
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/playground/samples/validation.js:
--------------------------------------------------------------------------------
1 | function validate({ pass1, pass2 }, errors) {
2 | if (pass1 !== pass2) {
3 | errors.pass2.addError("Passwords don't match.");
4 | }
5 | return errors;
6 | }
7 |
8 | function transformErrors(errors) {
9 | return errors.map(error => {
10 | if (error.name === "minimum" && error.property === "instance.age") {
11 | return Object.assign({}, error, {
12 | message: "You need to be 18 because of some legal thing",
13 | });
14 | }
15 | return error;
16 | });
17 | }
18 |
19 | export default {
20 | schema: {
21 | title: "Custom validation",
22 | description:
23 | "This form defines custom validation rules checking that the two passwords match.",
24 | type: "object",
25 | properties: {
26 | pass1: {
27 | title: "Password",
28 | type: "string",
29 | minLength: 3,
30 | },
31 | pass2: {
32 | title: "Repeat password",
33 | type: "string",
34 | minLength: 3,
35 | },
36 | age: {
37 | title: "Age",
38 | type: "number",
39 | minimum: 18,
40 | },
41 | },
42 | },
43 | uiSchema: {
44 | pass1: { "ui:widget": "password" },
45 | pass2: { "ui:widget": "password" },
46 | },
47 | formData: {},
48 | validate,
49 | transformErrors,
50 | };
51 |
--------------------------------------------------------------------------------
/src/components/widgets/index.js:
--------------------------------------------------------------------------------
1 | import AltDateWidget from "./AltDateWidget";
2 | import AltDateTimeWidget from "./AltDateTimeWidget";
3 | import BaseInput from "./BaseInput";
4 | import CheckboxWidget from "./CheckboxWidget";
5 | import CheckboxesWidget from "./CheckboxesWidget";
6 | import ColorWidget from "./ColorWidget";
7 | import DateWidget from "./DateWidget";
8 | import DateTimeWidget from "./DateTimeWidget";
9 | import EmailWidget from "./EmailWidget";
10 | import FileWidget from "./FileWidget";
11 | import HiddenWidget from "./HiddenWidget";
12 | import PasswordWidget from "./PasswordWidget";
13 | import RadioWidget from "./RadioWidget";
14 | import RangeWidget from "./RangeWidget";
15 | import SelectWidget from "./SelectWidget";
16 | import TextareaWidget from "./TextareaWidget";
17 | import TextWidget from "./TextWidget";
18 | import URLWidget from "./URLWidget";
19 | import UpDownWidget from "./UpDownWidget";
20 |
21 | export default {
22 | BaseInput,
23 | PasswordWidget,
24 | RadioWidget,
25 | UpDownWidget,
26 | RangeWidget,
27 | SelectWidget,
28 | TextWidget,
29 | DateWidget,
30 | DateTimeWidget,
31 | AltDateWidget,
32 | AltDateTimeWidget,
33 | EmailWidget,
34 | URLWidget,
35 | TextareaWidget,
36 | HiddenWidget,
37 | ColorWidget,
38 | FileWidget,
39 | CheckboxWidget,
40 | CheckboxesWidget,
41 | };
42 |
--------------------------------------------------------------------------------
/playground/samples/anyOf.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | type: "object",
4 | properties: {
5 | age: {
6 | type: "integer",
7 | title: "Age",
8 | },
9 | items: {
10 | type: "array",
11 | items: {
12 | type: "object",
13 | anyOf: [
14 | {
15 | properties: {
16 | foo: {
17 | type: "string",
18 | },
19 | },
20 | },
21 | {
22 | properties: {
23 | bar: {
24 | type: "string",
25 | },
26 | },
27 | },
28 | ],
29 | },
30 | },
31 | },
32 | anyOf: [
33 | {
34 | title: "First method of identification",
35 | properties: {
36 | firstName: {
37 | type: "string",
38 | title: "First name",
39 | default: "Chuck",
40 | },
41 | lastName: {
42 | type: "string",
43 | title: "Last name",
44 | },
45 | },
46 | },
47 | {
48 | title: "Second method of identification",
49 | properties: {
50 | idCode: {
51 | type: "string",
52 | title: "ID code",
53 | },
54 | },
55 | },
56 | ],
57 | },
58 | formData: {},
59 | };
60 |
--------------------------------------------------------------------------------
/playground/samples/date.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "Date and time widgets",
4 | type: "object",
5 | properties: {
6 | native: {
7 | title: "Native",
8 | description:
9 | "May not work on some browsers, notably Firefox Desktop and IE.",
10 | type: "object",
11 | properties: {
12 | datetime: {
13 | type: "string",
14 | format: "date-time",
15 | },
16 | date: {
17 | type: "string",
18 | format: "date",
19 | },
20 | },
21 | },
22 | alternative: {
23 | title: "Alternative",
24 | description: "These work on most platforms.",
25 | type: "object",
26 | properties: {
27 | "alt-datetime": {
28 | type: "string",
29 | format: "date-time",
30 | },
31 | "alt-date": {
32 | type: "string",
33 | format: "date",
34 | },
35 | },
36 | },
37 | },
38 | },
39 | uiSchema: {
40 | alternative: {
41 | "alt-datetime": {
42 | "ui:widget": "alt-datetime",
43 | "ui:options": {
44 | yearsRange: [1980, 2030],
45 | },
46 | },
47 | "alt-date": {
48 | "ui:widget": "alt-date",
49 | "ui:options": {
50 | yearsRange: [1980, 2030],
51 | },
52 | },
53 | },
54 | },
55 | formData: {},
56 | };
57 |
--------------------------------------------------------------------------------
/playground/samples/numbers.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | type: "object",
4 | title: "Number fields & widgets",
5 | properties: {
6 | number: {
7 | title: "Number",
8 | type: "number",
9 | },
10 | integer: {
11 | title: "Integer",
12 | type: "integer",
13 | },
14 | numberEnum: {
15 | type: "number",
16 | title: "Number enum",
17 | enum: [1, 2, 3],
18 | },
19 | numberEnumRadio: {
20 | type: "number",
21 | title: "Number enum",
22 | enum: [1, 2, 3],
23 | },
24 | integerRange: {
25 | title: "Integer range",
26 | type: "integer",
27 | minimum: 42,
28 | maximum: 100,
29 | },
30 | integerRangeSteps: {
31 | title: "Integer range (by 10)",
32 | type: "integer",
33 | minimum: 50,
34 | maximum: 100,
35 | multipleOf: 10,
36 | },
37 | },
38 | },
39 | uiSchema: {
40 | integer: {
41 | "ui:widget": "updown",
42 | },
43 | numberEnumRadio: {
44 | "ui:widget": "radio",
45 | "ui:options": {
46 | inline: true,
47 | },
48 | },
49 | integerRange: {
50 | "ui:widget": "range",
51 | },
52 | integerRangeSteps: {
53 | "ui:widget": "range",
54 | },
55 | },
56 | formData: {
57 | number: 3.14,
58 | integer: 42,
59 | numberEnum: 2,
60 | integerRange: 42,
61 | integerRangeSteps: 80,
62 | },
63 | };
64 |
--------------------------------------------------------------------------------
/test/const_test.js:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 |
3 | import { createFormComponent, createSandbox } from "./test_utils";
4 |
5 | describe("const", () => {
6 | let sandbox;
7 |
8 | beforeEach(() => {
9 | sandbox = createSandbox();
10 | });
11 |
12 | afterEach(() => {
13 | sandbox.restore();
14 | });
15 |
16 | it("should render a schema that uses const with a string value", () => {
17 | const schema = {
18 | type: "object",
19 | properties: {
20 | foo: { const: "bar" },
21 | },
22 | };
23 |
24 | const { node } = createFormComponent({
25 | schema,
26 | });
27 |
28 | expect(node.querySelector("input#root_foo")).not.eql(null);
29 | });
30 |
31 | it("should render a schema that uses const with a number value", () => {
32 | const schema = {
33 | type: "object",
34 | properties: {
35 | foo: { const: 123 },
36 | },
37 | };
38 |
39 | const { node } = createFormComponent({
40 | schema,
41 | });
42 |
43 | expect(node.querySelector("input#root_foo")).not.eql(null);
44 | });
45 |
46 | it("should render a schema that uses const with a boolean value", () => {
47 | const schema = {
48 | type: "object",
49 | properties: {
50 | foo: { const: true },
51 | },
52 | };
53 |
54 | const { node } = createFormComponent({
55 | schema,
56 | });
57 |
58 | expect(node.querySelector("input#root_foo[type='checkbox']")).not.eql(null);
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/test/NullField_test.js:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 |
3 | import { createFormComponent, createSandbox } from "./test_utils";
4 |
5 | describe("NullField", () => {
6 | let sandbox;
7 |
8 | beforeEach(() => {
9 | sandbox = createSandbox();
10 | });
11 |
12 | afterEach(() => {
13 | sandbox.restore();
14 | });
15 |
16 | describe("No widget", () => {
17 | it("should render a null field", () => {
18 | const { node } = createFormComponent({
19 | schema: {
20 | type: "null",
21 | },
22 | });
23 |
24 | expect(node.querySelectorAll(".field")).to.have.length.of(1);
25 | });
26 |
27 | it("should render a null field with a label", () => {
28 | const { node } = createFormComponent({
29 | schema: {
30 | type: "null",
31 | title: "foo",
32 | },
33 | });
34 |
35 | expect(node.querySelector(".field label").textContent).eql("foo");
36 | });
37 |
38 | it("should assign a default value", () => {
39 | const { comp } = createFormComponent({
40 | schema: {
41 | type: "null",
42 | default: null,
43 | },
44 | });
45 |
46 | expect(comp.state.formData).eql(null);
47 | });
48 |
49 | it("should not overwrite existing data", () => {
50 | const { comp } = createFormComponent({
51 | schema: {
52 | type: "null",
53 | },
54 | formData: 3,
55 | });
56 |
57 | expect(comp.state.formData).eql(3);
58 | });
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require("path");
2 | var webpack = require("webpack");
3 | var MiniCssExtractPlugin = require("mini-css-extract-plugin");
4 |
5 | module.exports = {
6 | mode: "production",
7 | entry: "./playground/app",
8 | output: {
9 | path: path.join(__dirname, "build"),
10 | filename: "bundle.js",
11 | publicPath: "/static/"
12 | },
13 | plugins: [
14 | new MiniCssExtractPlugin({filename: "styles.css", allChunks: true}),
15 | new webpack.DefinePlugin({
16 | "process.env": {
17 | NODE_ENV: JSON.stringify("production"),
18 | SHOW_NETLIFY_BADGE: JSON.stringify(process.env.SHOW_NETLIFY_BADGE)
19 | }
20 | })
21 | ],
22 | resolve: {
23 | extensions: [".js", ".jsx", ".css"]
24 | },
25 | module: {
26 | rules: [
27 | {
28 | test: /\.jsx?$/,
29 | use: [
30 | "babel-loader",
31 | ],
32 | include: [
33 | path.join(__dirname, "src"),
34 | path.join(__dirname, "playground"),
35 | path.join(__dirname, "node_modules", "codemirror", "mode", "javascript"),
36 | ],
37 | },
38 | {
39 | test: /\.css$/,
40 | use: [
41 | {
42 | loader: MiniCssExtractPlugin.loader,
43 | },
44 | "css-loader",
45 | ],
46 | include: [
47 | path.join(__dirname, "css"),
48 | path.join(__dirname, "playground"),
49 | path.join(__dirname, "node_modules"),
50 | ],
51 | }
52 | ]
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/playground/samples/references.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | definitions: {
4 | address: {
5 | type: "object",
6 | properties: {
7 | street_address: { type: "string" },
8 | city: { type: "string" },
9 | state: { type: "string" },
10 | },
11 | required: ["street_address", "city", "state"],
12 | },
13 | node: {
14 | type: "object",
15 | properties: {
16 | name: { type: "string" },
17 | children: {
18 | type: "array",
19 | items: {
20 | $ref: "#/definitions/node",
21 | },
22 | },
23 | },
24 | },
25 | },
26 | type: "object",
27 | properties: {
28 | billing_address: {
29 | title: "Billing address",
30 | $ref: "#/definitions/address",
31 | },
32 | shipping_address: {
33 | title: "Shipping address",
34 | $ref: "#/definitions/address",
35 | },
36 | tree: {
37 | title: "Recursive references",
38 | $ref: "#/definitions/node",
39 | },
40 | },
41 | },
42 | uiSchema: {
43 | "ui:order": ["shipping_address", "billing_address", "tree"],
44 | },
45 | formData: {
46 | billing_address: {
47 | street_address: "21, Jump Street",
48 | city: "Babel",
49 | state: "Neverland",
50 | },
51 | shipping_address: {
52 | street_address: "221B, Baker Street",
53 | city: "London",
54 | state: "N/A",
55 | },
56 | tree: {
57 | name: "root",
58 | children: [{ name: "leaf" }],
59 | },
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/test/DescriptionField_test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { expect } from "chai";
3 |
4 | import DescriptionField from "../src/components/fields/DescriptionField";
5 | import { createSandbox, createComponent } from "./test_utils";
6 |
7 | describe("DescriptionField", () => {
8 | let sandbox;
9 |
10 | beforeEach(() => {
11 | sandbox = createSandbox();
12 | });
13 |
14 | afterEach(() => {
15 | sandbox.restore();
16 | });
17 |
18 | // For some reason, stateless components needs to be wrapped into a stateful
19 | // one to be rendered into the document.
20 | class DescriptionFieldWrapper extends React.Component {
21 | constructor(props) {
22 | super(props);
23 | }
24 | render() {
25 | return ;
26 | }
27 | }
28 |
29 | it("should return a div for a custom component", () => {
30 | const props = {
31 | description: description,
32 | };
33 | const { node } = createComponent(DescriptionFieldWrapper, props);
34 |
35 | expect(node.tagName).to.equal("DIV");
36 | });
37 |
38 | it("should return a p for a description text", () => {
39 | const props = {
40 | description: "description",
41 | };
42 | const { node } = createComponent(DescriptionFieldWrapper, props);
43 |
44 | expect(node.tagName).to.equal("P");
45 | });
46 |
47 | it("should have the expected id", () => {
48 | const props = {
49 | description: "Field description",
50 | id: "sample_id",
51 | };
52 | const { node } = createComponent(DescriptionFieldWrapper, props);
53 |
54 | expect(node.id).to.equal("sample_id");
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/playground/samples/simple.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "A registration form",
4 | description: "A simple form example.",
5 | type: "object",
6 | required: ["firstName", "lastName"],
7 | properties: {
8 | firstName: {
9 | type: "string",
10 | title: "First name",
11 | default: "Chuck",
12 | },
13 | lastName: {
14 | type: "string",
15 | title: "Last name",
16 | },
17 | age: {
18 | type: "integer",
19 | title: "Age",
20 | },
21 | bio: {
22 | type: "string",
23 | title: "Bio",
24 | },
25 | password: {
26 | type: "string",
27 | title: "Password",
28 | minLength: 3,
29 | },
30 | telephone: {
31 | type: "string",
32 | title: "Telephone",
33 | minLength: 10,
34 | },
35 | },
36 | },
37 | uiSchema: {
38 | firstName: {
39 | "ui:autofocus": true,
40 | "ui:emptyValue": "",
41 | },
42 | age: {
43 | "ui:widget": "updown",
44 | "ui:title": "Age of person",
45 | "ui:description": "(earthian year)",
46 | },
47 | bio: {
48 | "ui:widget": "textarea",
49 | },
50 | password: {
51 | "ui:widget": "password",
52 | "ui:help": "Hint: Make it strong!",
53 | },
54 | date: {
55 | "ui:widget": "alt-datetime",
56 | },
57 | telephone: {
58 | "ui:options": {
59 | inputType: "tel",
60 | },
61 | },
62 | },
63 | formData: {
64 | lastName: "Norris",
65 | age: 75,
66 | bio: "Roundhouse kicking asses since 1940",
67 | password: "noneed",
68 | },
69 | };
70 |
--------------------------------------------------------------------------------
/playground/samples/customArray.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function ArrayFieldTemplate(props) {
4 | return (
5 |
6 | {props.items &&
7 | props.items.map(element => (
8 |
9 |
{element.children}
10 | {element.hasMoveDown && (
11 |
18 | )}
19 | {element.hasMoveUp && (
20 |
27 | )}
28 |
31 |
32 |
33 | ))}
34 |
35 | {props.canAdd && (
36 |
37 |
38 |
41 |
42 |
43 | )}
44 |
45 | );
46 | }
47 |
48 | export default {
49 | schema: {
50 | title: "Custom array of strings",
51 | type: "array",
52 | items: {
53 | type: "string",
54 | },
55 | },
56 | formData: ["react", "jsonschema", "form"],
57 | ArrayFieldTemplate,
58 | };
59 |
--------------------------------------------------------------------------------
/playground/samples/index.js:
--------------------------------------------------------------------------------
1 | import arrays from "./arrays";
2 | import anyOf from "./anyOf";
3 | import oneOf from "./oneOf";
4 | import nested from "./nested";
5 | import numbers from "./numbers";
6 | import simple from "./simple";
7 | import widgets from "./widgets";
8 | import ordering from "./ordering";
9 | import references from "./references";
10 | import custom from "./custom";
11 | import errors from "./errors";
12 | import large from "./large";
13 | import date from "./date";
14 | import validation from "./validation";
15 | import files from "./files";
16 | import single from "./single";
17 | import customArray from "./customArray";
18 | import customObject from "./customObject";
19 | import alternatives from "./alternatives";
20 | import propertyDependencies from "./propertyDependencies";
21 | import schemaDependencies from "./schemaDependencies";
22 | import additionalProperties from "./additionalProperties";
23 | import nullable from "./nullable";
24 | import nullField from "./null";
25 |
26 | export const samples = {
27 | Simple: simple,
28 | Nested: nested,
29 | Arrays: arrays,
30 | Numbers: numbers,
31 | Widgets: widgets,
32 | Ordering: ordering,
33 | References: references,
34 | Custom: custom,
35 | Errors: errors,
36 | Large: large,
37 | "Date & time": date,
38 | Validation: validation,
39 | Files: files,
40 | Single: single,
41 | "Custom Array": customArray,
42 | "Custom Object": customObject,
43 | Alternatives: alternatives,
44 | "Property dependencies": propertyDependencies,
45 | "Schema dependencies": schemaDependencies,
46 | "Additional Properties": additionalProperties,
47 | "Any Of": anyOf,
48 | "One Of": oneOf,
49 | "Null fields": nullField,
50 | Nullable: nullable,
51 | };
52 |
--------------------------------------------------------------------------------
/playground/samples/customObject.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function ObjectFieldTemplate({ TitleField, properties, title, description }) {
4 | return (
5 |
6 |
7 |
8 | {properties.map(prop => (
9 |
12 | {prop.content}
13 |
14 | ))}
15 |
16 | {description}
17 |
18 | );
19 | }
20 |
21 | export default {
22 | schema: {
23 | title: "A registration form",
24 | description:
25 | "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.",
26 | type: "object",
27 | required: ["firstName", "lastName"],
28 | properties: {
29 | firstName: {
30 | type: "string",
31 | title: "First name",
32 | },
33 | lastName: {
34 | type: "string",
35 | title: "Last name",
36 | },
37 | age: {
38 | type: "integer",
39 | title: "Age",
40 | },
41 | bio: {
42 | type: "string",
43 | title: "Bio",
44 | },
45 | password: {
46 | type: "string",
47 | title: "Password",
48 | minLength: 3,
49 | },
50 | telephone: {
51 | type: "string",
52 | title: "Telephone",
53 | minLength: 10,
54 | },
55 | },
56 | },
57 | formData: {
58 | firstName: "Chuck",
59 | lastName: "Norris",
60 | age: 75,
61 | bio: "Roundhouse kicking asses since 1940",
62 | password: "noneed",
63 | },
64 | ObjectFieldTemplate,
65 | };
66 |
--------------------------------------------------------------------------------
/playground/samples/nested.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | schema: {
3 | title: "A list of tasks",
4 | type: "object",
5 | required: ["title"],
6 | properties: {
7 | title: {
8 | type: "string",
9 | title: "Task list title",
10 | },
11 | tasks: {
12 | type: "array",
13 | title: "Tasks",
14 | items: {
15 | type: "object",
16 | required: ["title"],
17 | properties: {
18 | title: {
19 | type: "string",
20 | title: "Title",
21 | description: "A sample title",
22 | },
23 | details: {
24 | type: "string",
25 | title: "Task details",
26 | description: "Enter the task details",
27 | },
28 | done: {
29 | type: "boolean",
30 | title: "Done?",
31 | default: false,
32 | },
33 | },
34 | },
35 | },
36 | },
37 | },
38 | uiSchema: {
39 | tasks: {
40 | items: {
41 | details: {
42 | "ui:widget": "textarea",
43 | },
44 | },
45 | },
46 | },
47 | formData: {
48 | title: "My current tasks",
49 | tasks: [
50 | {
51 | title: "My first task",
52 | details:
53 | "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
54 | done: true,
55 | },
56 | {
57 | title: "My second task",
58 | details:
59 | "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur",
60 | done: false,
61 | },
62 | ],
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/src/components/widgets/TextareaWidget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | function TextareaWidget(props) {
5 | const {
6 | id,
7 | options,
8 | placeholder,
9 | value,
10 | required,
11 | disabled,
12 | readonly,
13 | autofocus,
14 | onChange,
15 | onBlur,
16 | onFocus,
17 | } = props;
18 | const _onChange = ({ target: { value } }) => {
19 | return onChange(value === "" ? options.emptyValue : value);
20 | };
21 | return (
22 |