diff --git a/.editorconfig b/.editorconfig index 2c01c14027..38fb58c899 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,5 @@ +[*] + indent_style = space indent_size = 2 charset = utf-8 diff --git a/README.md b/README.md index 1ab66e6cfa..9a91a6bc64 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,18 @@ yarn dev yarn dev:watch ``` -### Dist and publish +### Dist ```sh yarn build ``` +### Publish + +```sh +yarn release +``` + ## Live Playground A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) from the original repository, is hosted on gh-pages. @@ -46,6 +52,7 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) f - [Form error event handler](#form-error-event-handler) - [Form data changes](#form-data-changes) - [Form field blur events](#form-field-blur-events) + - [Form field focus events](#form-field-focus-events) - [Form customization](#form-customization) - [The uiSchema object](#the-uischema-object) - [Alternative widgets](#alternative-widgets) @@ -66,7 +73,8 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) f - [removable option](#removable-option) - [Custom CSS class names](#custom-css-class-names) - [Custom labels for enum fields](#custom-labels-for-enum-fields) - - [Disabled enum fields](#disabled-attribute-for-enum-fields) + - [Alternative JSON-Schema compliant approach](#alternative-json-schema-compliant-approach) + - [Disabled attribute for enum fields](#disabled-attribute-for-enum-fields) - [Multiple choices list](#multiple-choices-list) - [Autogenerated widget ids](#autogenerated-widget-ids) - [Form action buttons](#form-action-buttons) @@ -84,11 +92,12 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) f - [Array Field Template](#array-field-template) - [Object Field Template](#object-field-template) - [Error List template](#error-list-template) + - [Id prefix](#id-prefix) - [Custom widgets and fields](#custom-widgets-and-fields) - [Custom widget components](#custom-widget-components) - [Custom component registration](#custom-component-registration) - [Custom widget options](#custom-widget-options) - - [Customizing widgets' text input](#customizing-widgets-text-input) + - [Customizing widgets text input](#customizing-widgets-text-input) - [Custom field components](#custom-field-components) - [Field props](#field-props) - [The registry object](#the-registry-object) @@ -108,11 +117,11 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) f - [Styling your forms](#styling-your-forms) - [Schema definitions and references](#schema-definitions-and-references) - [Property dependencies](#property-dependencies) - - [Unidirectional](#unidirectional) - - [Bidirectional](#bidirectional) - - [Schema dependencies](#schema-depdencies) - - [Conditional](#conditional) - - [Dynamic](#dynamic) + - [Unidirectional](#unidirectional) + - [Bidirectional](#bidirectional) + - [Schema dependencies](#schema-dependencies) + - [Conditional](#conditional) + - [Dynamic](#dynamic) - [JSON Schema supporting status](#json-schema-supporting-status) - [Tips and tricks](#tips-and-tricks) - [Contributing](#contributing) @@ -120,14 +129,17 @@ A [live playground](https://mozilla-services.github.io/react-jsonschema-form/) f - [Development server](#development-server) - [Tests](#tests) - [TDD](#tdd) + - [Releasing](#releasing) - [FAQ](#faq) + - [Q: Does rjsf support oneOf, anyOf, multiple types in an array, etc.?](#q-does-rjsf-support-oneof-anyof-multiple-types-in-an-array-etc) + - [Q: Will react-jsonschema-form support Material, Ant-Design, Foundation, or [some other specific widget library or frontend style]?](#q-will-react-jsonschema-form-support-material-ant-design-foundation-or-some-other-specific-widget-library-or-frontend-style) - [License](#license) --- ## Philosophy -react-jsonschema-form is meant to automatically generate a React form based on a [JSON Schema](http://json-schema.org/). It is a major component in the [kinto-admin](https://github.com/Kinto/kinto-admin/). If you want to generate a form for any data, sight unseen, simply given a JSON schema, react-jsonschema-form may be for you. If you have a priori knowledge of your data and want a toolkit for generating forms for it, you might look elsewhere. +react-jsonschema-form is meant to automatically generate a React form based on a [JSON Schema](http://json-schema.org/). It is a major component in the [kinto-admin](https://github.com/Kinto/kinto-admin/). If you want to generate a form for any data, sight unseen, simply given a JSON schema, react-jsonschema-form may be for you. If you have _a priori_ knowledge of your data and want a toolkit for generating forms for it, you might look elsewhere. react-jsonschema-form validates that the data conforms to the given schema, but doesn't prevent the user from inputing data that doesn't fit (for example, stripping non-numbers from a number field, or not letting the user add values to an array that is already "full". @@ -225,7 +237,7 @@ WARNING: If you have situations where your parent component can re-render, make You can pass a function as the `onSubmit` prop of your `Form` component to listen to when the form is submitted and its data are valid. It will be passed a result object having a `formData` attribute, which is the valid form data you're usually after: ```js -const onSubmit = ({formData}) => console.log("yay I'm valid!"); +const onSubmit = ({formData}) => console.log("Data submitted: ", formData); render((
+ + + + +``` + +A live example of both approaches side-by-side can be found in the **Alternatives** playground preset. + ### Disabled attribute for `enum` fields -This library supports the 'disbaled' attribute for `enum` options.Enum disabled allows disabling options for 'enum' fields.This attribute can be added as a part of uiSchema. +This library supports the 'disabled' attribute for `enum` options. Enum disabled allows disabling options for 'enum' fields.This attribute can be added as a part of uiSchema. ```js const schema = { @@ -873,6 +930,7 @@ The following props are passed to a custom field template component: - `hidden`: A boolean value stating if the field should be hidden. - `required`: A boolean value stating if the field is required. - `readonly`: A boolean value stating if the field is read-only. +- `disabled`: A boolean value stating if the field is disabled. - `displayLabel`: A boolean value stating if the label should be rendered or not. This is useful for nested fields in arrays where you don't want to clutter the UI. - `fields`: An array containing all Form's fields including your [custom fields](#custom-field-components) and the built-in fields. - `schema`: The schema object for this field. @@ -913,7 +971,7 @@ The following props are passed to each `ArrayFieldTemplate`: - `disabled`: A boolean value stating if the array is disabled. - `idSchema`: Object - `items`: An array of objects representing the items in the array. Each of the items represent a child with properties described below. -- `onAddClick: (event) => (event) => void`: Returns a function that adds a new item to the array. +- `onAddClick: (event) => void`: A function that adds a new item to the array. - `readonly`: A boolean value stating if the array is read-only. - `required`: A boolean value stating if the array is required. - `schema`: The schema object for this array. @@ -1020,6 +1078,19 @@ The following props are passed to `ErrorList` - `uiSchema`: The uiSchema that was passed to `Form`. - `formContext`: The `formContext` object that you passed to Form. +### Id prefix + +To avoid collisions with existing ids in the DOM, it is possible to change the prefix used for ids (the default is `root`). + +```jsx +render(( + , +), document.getElementById("app")); +``` + +This will render `` instead of `` + ### Custom widgets and fields The API allows to specify your own custom *widget* and *field* components: @@ -1149,7 +1220,7 @@ render(( > Note: Since v0.41.0, the `ui:widget` object API, where a widget and options were specified with `"ui:widget": {component, options}` shape, is deprecated. It will be removed in a future release. -#### Customizing widgets' text input +#### Customizing widgets text input All the widgets that render a text input use the `BaseInput` component internally. If you need to customize all text inputs without customizing all widgets individially, you can provide a `BaseInput` component in the `widgets` property of `Form` (see [Custom component registration](#custom-component-registration). @@ -1251,7 +1322,6 @@ i.glyphicon { display: none; } .array-item-move-up::after { content: 'Move Up'; } .array-item-move-down::after { content: 'Move Down'; } .array-item-remove::after { content: 'Remove'; } -} ``` ### Custom SchemaField @@ -1695,7 +1765,7 @@ This component follows [JSON Schema](http://json-schema.org/documentation.html) 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"]` Nobody yet has come up with a PR that adds this feature with a simple and easy-to-understand UX. - You can use `oneOf` with [schema dependencies](#schema-depdencies) 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 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. ## Tips and tricks @@ -1767,7 +1837,7 @@ $ git push --tags origin ### Q: Does rjsf support `oneOf`, `anyOf`, multiple types in an array, etc.? -A: Not yet (except for a special case where you can use `oneOf` in [schema dependencies](#schema-depdencies)), but perhaps you will be the person whose PR will finally add the feature in a way that gets merged. For inspiration, see [#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). +A: Not yet (except for a special case where you can use `oneOf` in [schema dependencies](#schema-dependencies)), but perhaps you will be the person whose PR will finally add the feature in a way that gets merged. For inspiration, see [#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]? diff --git a/package.json b/package.json index 6a4b644a49..9ae5cfd6c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@carecloud/react-jsonschema-form", - "version": "0.29.0", + "version": "1.0.0", "description": "A simple React component capable of building HTML forms out of a JSON schema.", "scripts": { "dev": "rimraf lib && cross-env NODE_ENV=production babel -d lib/ src/", @@ -13,6 +13,9 @@ "prettier": "prettier --write", "prettier:all": "prettier --write 'src/**/*.{js,jsx,css,scss}'", "publish-to-gh-pages": "npm run build:playground && gh-pages --dist build/", + "release": "yarn build && npm publish", + "preversion": "npm run build:playground && npm run dist && npm run build:readme && npm run cs-check && npm run lint", + "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" }, @@ -86,10 +89,11 @@ "json-loader": "^0.5.7", "lint-staged": "^3.3.1", "mocha": "^2.5.3", - "prettier": "^1.7.2", + "prettier": "^1.12.0", "react-addons-test-utils": "^15.3.2", - "react-codemirror2": "^2.0.2", - "react-dom": "16.2.0", + "react-codemirror2": "^4.1.0", + "react": "^16.2.0", + "react-dom": "^16.2.0", "react-transform-catch-errors": "^1.0.0", "react-transform-hmr": "^1.0.4", "redbox-react": "^1.3.3", diff --git a/playground/app.js b/playground/app.js index 93ed968a27..36a3eab737 100644 --- a/playground/app.js +++ b/playground/app.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { render } from 'react-dom'; -import CodeMirror from 'react-codemirror2'; +import { UnControlled as CodeMirror } from 'react-codemirror2'; import 'codemirror/mode/javascript/javascript'; import { shouldRender } from '../src/utils'; @@ -193,6 +193,7 @@ class Editor extends Component { @@ -355,7 +356,9 @@ class App extends Component { onShare = () => { const { formData, schema, uiSchema } = this.state; - const { location: { origin, pathname } } = document; + const { + location: { origin, pathname }, + } = document; try { const hash = btoa(JSON.stringify({ formData, schema, uiSchema })); this.setState({ shareURL: `${origin}${pathname}#${hash}` }); @@ -435,11 +438,17 @@ class App extends Component { uiSchema={uiSchema} formData={formData} onChange={this.onFormDataChange} - onSubmit={({ formData }) => console.log('submitted formData', formData)} + onSubmit={({ formData }) => + console.log("submitted formData", formData) + } fields={{ geo: GeoPosition }} validate={validate} - onBlur={(id, value) => console.log(`Touched ${id} with value ${value}`)} - onFocus={(id, value) => console.log(`Focused ${id} with value ${value}`)} + onBlur={(id, value) => + console.log(`Touched ${id} with value ${value}`) + } + onFocus={(id, value) => + console.log(`Focused ${id} with value ${value}`) + } transformErrors={transformErrors} onError={log('errors')}>
diff --git a/src/components/Form.js b/src/components/Form.js index 17610fc755..602e997127 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -4,12 +4,13 @@ import PropTypes from 'prop-types'; import { default as DefaultErrorList } from './ErrorList'; import { getDefaultFormState, + retrieveSchema, shouldRender, toIdSchema, setState, getDefaultRegistry, } from '../utils'; -import validateFormData from '../validate'; +import validateFormData, { toErrorList } from '../validate'; export default class Form extends Component { static defaultProps = { @@ -39,13 +40,21 @@ export default class Form extends Component { const mustValidate = edit && !props.noValidate && liveValidate; const { definitions } = schema; const formData = getDefaultFormState(schema, props.formData, definitions); + const retrievedSchema = retrieveSchema(schema, definitions, formData); + const { errors, errorSchema } = mustValidate ? this.validate(formData, schema) : { errors: state.errors || [], errorSchema: state.errorSchema || {}, }; - const idSchema = toIdSchema(schema, uiSchema['ui:rootFieldId'], definitions, formData); + const idSchema = toIdSchema( + retrievedSchema, + uiSchema['ui:rootFieldId'], + definitions, + formData, + props.idPrefix + ); return { schema, uiSchema, @@ -61,9 +70,11 @@ export default class Form extends Component { return shouldRender(this, nextProps, nextState); } - validate(formData, schema) { + validate(formData, schema = this.props.schema) { const { validate, transformErrors } = this.props; - return validateFormData(formData, schema || this.props.schema, validate, transformErrors); + const { definitions } = this.getRegistry(); + const resolvedSchema = retrieveSchema(schema, definitions, formData); + return validateFormData(formData, resolvedSchema, validate, transformErrors); } renderErrors() { @@ -84,12 +95,18 @@ export default class Form extends Component { return null; } - onChange = (formData, options = { validate: false }) => { - const mustValidate = !this.props.noValidate && (this.props.liveValidate || options.validate); + onChange = (formData, newErrorSchema) => { + const mustValidate = !this.props.noValidate && this.props.liveValidate; let state = { formData }; if (mustValidate) { const { errors, errorSchema } = this.validate(formData); state = { ...state, errors, errorSchema }; + } else if (!this.props.noValidate && newErrorSchema) { + state = { + ...state, + errorSchema: newErrorSchema, + errors: toErrorList(newErrorSchema), + }; } setState(this, state, () => { if (this.props.onChange) { @@ -153,6 +170,7 @@ export default class Form extends Component { children, safeRenderCompletion, id, + idPrefix, className, name, method, @@ -180,13 +198,15 @@ export default class Form extends Component { encType={enctype} acceptCharset={acceptcharset} noValidate={noHtml5Validate} - onSubmit={this.onSubmit}> + onSubmit={this.onSubmit} + > {this.renderErrors()} <_SchemaField schema={schema} uiSchema={uiSchema} errorSchema={errorSchema} idSchema={idSchema} + idPrefix={idPrefix} formData={formData} onChange={this.onChange} onBlur={this.onBlur} diff --git a/src/components/fields/ArrayField.js b/src/components/fields/ArrayField.js index 701b0a8dea..514eccb092 100644 --- a/src/components/fields/ArrayField.js +++ b/src/components/fields/ArrayField.js @@ -54,7 +54,13 @@ function DefaultArrayItem(props) { {props.hasToolbar && (
-
+
{(props.hasMoveUp || props.hasMoveDown) && (