From 562681317fb25279fa27afdb70ebb4af760244e2 Mon Sep 17 00:00:00 2001 From: Dan Balarin Date: Mon, 18 Mar 2019 18:17:10 +0100 Subject: [PATCH 01/13] Added withTheme HOC --- src/index.js | 2 ++ src/withTheme.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/withTheme.js diff --git a/src/index.js b/src/index.js index ee2b982967..dc27c126d7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ import Form from "./components/Form"; +import withTheme from './withTheme'; +export {withTheme} export default Form; diff --git a/src/withTheme.js b/src/withTheme.js new file mode 100644 index 0000000000..f0d924c2e7 --- /dev/null +++ b/src/withTheme.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react' +import PropTypes from "prop-types"; +import Form from "react-jsonschema-form"; + +function withTheme(data) { + return class extends Component { + render() { + let {templates, widgets, fields, ...restData} = this.props + templates = {...data.templates, ...templates} + widgets = {...data.widgets, ...widgets} + fields = {...data.Fields, ...fields} + let ThemedForm = Form; + if(data.form) { + ThemedForm = data.form; + } + return + } + } +} + +withTheme.propTypes = { + form: PropTypes.object, + widgets: PropTypes.object, + fields: PropTypes.object, + templates: PropTypes.object, +}; + +export default withTheme; \ No newline at end of file From f6a43bb37e88b52deafb375f7262cb161dc79104 Mon Sep 17 00:00:00 2001 From: Dan Balarin Date: Mon, 18 Mar 2019 18:59:40 +0100 Subject: [PATCH 02/13] prettier run --- src/index.js | 4 ++-- src/withTheme.js | 45 ++++++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/index.js b/src/index.js index dc27c126d7..0e36f93a54 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import Form from "./components/Form"; -import withTheme from './withTheme'; +import withTheme from "./withTheme"; -export {withTheme} +export { withTheme }; export default Form; diff --git a/src/withTheme.js b/src/withTheme.js index f0d924c2e7..e1c4fd7c33 100644 --- a/src/withTheme.js +++ b/src/withTheme.js @@ -1,28 +1,35 @@ -import React, { Component } from 'react' +import React, { Component } from "react"; import PropTypes from "prop-types"; -import Form from "react-jsonschema-form"; +import Form from "./"; function withTheme(data) { - return class extends Component { - render() { - let {templates, widgets, fields, ...restData} = this.props - templates = {...data.templates, ...templates} - widgets = {...data.widgets, ...widgets} - fields = {...data.Fields, ...fields} - let ThemedForm = Form; - if(data.form) { - ThemedForm = data.form; - } - return - } + return class extends Component { + render() { + let { templates, widgets, fields, ...restData } = this.props; + templates = { ...data.templates, ...templates }; + widgets = { ...data.widgets, ...widgets }; + fields = { ...data.Fields, ...fields }; + let ThemedForm = Form; + if (data.form) { + ThemedForm = data.form; + } + return ( + + ); } + }; } withTheme.propTypes = { - form: PropTypes.object, - widgets: PropTypes.object, - fields: PropTypes.object, - templates: PropTypes.object, + form: PropTypes.object, + widgets: PropTypes.object, + fields: PropTypes.object, + templates: PropTypes.object, }; -export default withTheme; \ No newline at end of file +export default withTheme; From d1580e28801bcd3b17337e5d6f332c8fb3ce7055 Mon Sep 17 00:00:00 2001 From: Dan Balarin Date: Wed, 20 Mar 2019 20:07:16 +0100 Subject: [PATCH 03/13] renamed restData variable --- src/withTheme.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/withTheme.js b/src/withTheme.js index e1c4fd7c33..cc154fdf94 100644 --- a/src/withTheme.js +++ b/src/withTheme.js @@ -5,7 +5,7 @@ import Form from "./"; function withTheme(data) { return class extends Component { render() { - let { templates, widgets, fields, ...restData } = this.props; + let { templates, widgets, fields, ...otherProps } = this.props; templates = { ...data.templates, ...templates }; widgets = { ...data.widgets, ...widgets }; fields = { ...data.Fields, ...fields }; @@ -15,7 +15,7 @@ function withTheme(data) { } return ( Date: Wed, 20 Mar 2019 20:30:09 +0100 Subject: [PATCH 04/13] data.Fields -> data.fields --- src/withTheme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/withTheme.js b/src/withTheme.js index cc154fdf94..082ca33356 100644 --- a/src/withTheme.js +++ b/src/withTheme.js @@ -8,7 +8,7 @@ function withTheme(data) { let { templates, widgets, fields, ...otherProps } = this.props; templates = { ...data.templates, ...templates }; widgets = { ...data.widgets, ...widgets }; - fields = { ...data.Fields, ...fields }; + fields = { ...data.fields, ...fields }; let ThemedForm = Form; if (data.form) { ThemedForm = data.form; From a95c11d9a427bf80d6be0513db6dab3e88f7b1fd Mon Sep 17 00:00:00 2001 From: Dan Balarin Date: Wed, 20 Mar 2019 21:25:32 +0100 Subject: [PATCH 05/13] Created documentation --- docs/theme-customization.md | 160 ++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 161 insertions(+) create mode 100644 docs/theme-customization.md diff --git a/docs/theme-customization.md b/docs/theme-customization.md new file mode 100644 index 0000000000..e2163bdb2e --- /dev/null +++ b/docs/theme-customization.md @@ -0,0 +1,160 @@ +## Customizing with other frameworks + +### withTheme Higher-Order Component +`withTheme` component provides easy way to change appearence of form, widgets, templates and fields by using theme object. This object is passed as the only parameter like so: `withTheme(ThemeObj)` and it returns component which you use instead of standard `Form` component. + +### Usage + +```jsx +import React, { Component } from 'react' +import { withTheme } from 'react-jsonschema-form' +import Bootstrap4Theme from 'react-jsonschema-form-theme-bs4' + +class Demo extends Component { + render() { + const ThemedForm = withTheme(Bootstrap4Theme); + return + } +} +``` +*(you have to provide schemaObj and uiSchemaObj, these are excluded from example usage in favor of simplicity)* + +### Theme object +Theme object consists of **widgets**, **templates**, **fields** and **form**, none of them is required, atleast one should be provided though. + +#### widgets and fields +**widgets** and **fields** should be in exact format as show in [here](/advanced-customization/#custom-widgets-and-fields). + +example theme with custom widget: +```jsx +const MyCustomWidget = (props) => { + return ( + props.onChange(event.target.value)} /> + ); +}; + +const myWidgets = { + myCustomWidget: MyCustomWidget +}; + +const ThemeObject = {widgets: myWidgets}; +export default ThemeObject; +``` +similarly for **fields** + +#### templates +**templates** should be object containing template objects which gets spread (using spread operator, like so `
`). In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in theme object: +```jsx +function MyArrayFieldTemplate(props) { + return ( +
+ {props.items.map(element => element.children)} + {props.canAdd && } +
+ ); +} + +function MyErrorListTemplate(props) { + const {errors} = props; + return ( +
    + {errors.map(error => ( +
  • + {error.stack} +
  • + ))} +
+ ); +} + +const myTemplates = { + ArrayFieldTemplate: MyArrayFieldTemplate, + ErrorList: MyErrorListTemplate, +}; + +const ThemeObject = { + templates: myTemplates +}; + +export default ThemeObject; +``` + +#### form +Theme can also provide custom form component. + +**Requirments** + + - Custom form have to be valid React component (extends `React.Component` class and implements `render()` method) + - Overrided callback functions *(onSubmit, onError, onChange)*, have to call according callback with appropriate data from `this.props` + - Somewhere in render tree of this component have to be `Form` component + +**Example** + +This can be usefull whenever you need another layer of data handling on top of `Form` component but outside of your app logic. One example could be Bootstrap 4 theme with input like so: +```jsx +function MyBaseInput(props) { + ... + return ( + onBlur(inputProps.id, event.target.value))} + onFocus={onFocus && (event => onFocus(inputProps.id, event.target.value))} + /> + ) +} +``` +Let's say this is override of BaseInput, so any *text*, *textarea*, *number*, ..., inputs looks like this, and we want to have **valid** tag only after form was submitted (otherwise it could show red and green borders around inputs, right after user sees it, which is bad practice), so we have `wasValidated` variable in form context, which gets updated. Below is example of customized ``: +```jsx +import Form from "react-jsonschema-form"; +class Bootstrap4Form extends Component { + constructor(props) { + super(props); + this.state = {wasValidated: false}; + this.onSubmit = this.onSubmit.bind(this); + this.onError = this.onError.bind(this); + } + + onSubmit({...data}) { + this.setState({wasValidated: true}); + if(this.props.onSubmit) { + this.props.onSubmit({...data}); + } + } + + onError({...data}) { + this.setState({wasValidated: true}); + if(this.props.onError) { + this.props.onError({...data}); + } + } + + render() { + const {onSubmit, onError, ...otherProps} = this.props; + return ( + + ) + } +} +``` +and theme would look like this: +```jsx +const Bootstrap4Theme = { + form: Bootstrap4Form, + widgets: {BaseInput: MyBaseInput}, +} +export default Bootstrap4Theme +``` + +### Overriding +As well as theme can override **widgets**, **templates**, **fields** and **form**, you can override **widgets**, **templates**, **fields** just as usual. So user has higher priority than theme and theme higher than default values (**User**>**Theme**>**Defaults**). \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index d3d8fcbdcc..f32c08675d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,7 @@ nav: - Definitions: definitions.md - Dependencies: dependencies.md - Form Customization: form-customization.md + - Customizing with other frameworks: theme-customization.md - Validation: validation.md - Playground: https://mozilla-services.github.io/react-jsonschema-form/ From 58f19f203719733221405bdbee054c5dfd727efb Mon Sep 17 00:00:00 2001 From: Ashwin Ramaswami Date: Sun, 7 Apr 2019 14:17:49 -0700 Subject: [PATCH 06/13] doc: update doc --- docs/theme-customization.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/theme-customization.md b/docs/theme-customization.md index e2163bdb2e..081dc767e5 100644 --- a/docs/theme-customization.md +++ b/docs/theme-customization.md @@ -1,31 +1,30 @@ ## Customizing with other frameworks ### withTheme Higher-Order Component -`withTheme` component provides easy way to change appearence of form, widgets, templates and fields by using theme object. This object is passed as the only parameter like so: `withTheme(ThemeObj)` and it returns component which you use instead of standard `Form` component. +`withTheme` component provides a way to extend the functionality of react-jsonschema-form by passing in a theme object, which contains an easy way to change the appearance of the form, widgets, templates and fields by passing in theme object. This object is passed as the only parameter like so: `withTheme(ThemeObj)` and it returns component which you use instead of standard `Form` component. ### Usage ```jsx -import React, { Component } from 'react' -import { withTheme } from 'react-jsonschema-form' -import Bootstrap4Theme from 'react-jsonschema-form-theme-bs4' +import React, { Component } from 'react'; +import { withTheme } from 'react-jsonschema-form'; +import Bootstrap4Theme from 'react-jsonschema-form-theme-bs4'; +const ThemedForm = withTheme(Bootstrap4Theme); class Demo extends Component { render() { - const ThemedForm = withTheme(Bootstrap4Theme); - return + return } } ``` -*(you have to provide schemaObj and uiSchemaObj, these are excluded from example usage in favor of simplicity)* ### Theme object -Theme object consists of **widgets**, **templates**, **fields** and **form**, none of them is required, atleast one should be provided though. +The Theme object consists of the properties **widgets**, **templates**, **fields** and **form**. The form merges each prop's value with the default value for it; for example, providing a single widget in **widgets** will merge it with If one of these properties is not specified, the form reverts to the default None are required, although at least one should be provided though. #### widgets and fields -**widgets** and **fields** should be in exact format as show in [here](/advanced-customization/#custom-widgets-and-fields). +**widgets** and **fields** should be in the same format as shown [here](/advanced-customization/#custom-widgets-and-fields). -example theme with custom widget: +Example theme with custom widget: ```jsx const MyCustomWidget = (props) => { return ( @@ -44,10 +43,11 @@ const myWidgets = { const ThemeObject = {widgets: myWidgets}; export default ThemeObject; ``` -similarly for **fields** + +The above can similarly be done for **fields**. #### templates -**templates** should be object containing template objects which gets spread (using spread operator, like so ``). In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in theme object: +**templates** should be an object containing template objects which gets spread (using spread operator, like so ``). In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in theme object: ```jsx function MyArrayFieldTemplate(props) { return ( From 3e4e16f4e45296512ffbb9e3cb55fcd53350cff9 Mon Sep 17 00:00:00 2001 From: Ashwin Ramaswami Date: Sun, 7 Apr 2019 14:17:53 -0700 Subject: [PATCH 07/13] test: add test --- test/withTheme_test.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/withTheme_test.js diff --git a/test/withTheme_test.js b/test/withTheme_test.js new file mode 100644 index 0000000000..359ca0505a --- /dev/null +++ b/test/withTheme_test.js @@ -0,0 +1,34 @@ +import { expect } from "chai"; +import React from "react"; + +import { withTheme } from "../src"; +import { createComponent, createSandbox } from "./test_utils"; + +describe.only("withTheme", () => { + let sandbox; + + beforeEach(() => { + sandbox = createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe("With widgets", () => { + it("should use the provided widgets", () => { + const widgets = { + TextWidget: () =>
, + }; + const schema = { + type: "string", + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ widgets }), { + schema, + uiSchema, + }); + expect(node.querySelectorAll("#test")).to.have.length.of(1); + }); + }); +}); From 88b7907c2680900871aa23385fce761402173e94 Mon Sep 17 00:00:00 2001 From: Dan Balarin Date: Mon, 13 May 2019 15:41:47 +0200 Subject: [PATCH 08/13] removed custom form --- src/withTheme.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/withTheme.js b/src/withTheme.js index 082ca33356..2793913149 100644 --- a/src/withTheme.js +++ b/src/withTheme.js @@ -9,12 +9,8 @@ function withTheme(data) { templates = { ...data.templates, ...templates }; widgets = { ...data.widgets, ...widgets }; fields = { ...data.fields, ...fields }; - let ThemedForm = Form; - if (data.form) { - ThemedForm = data.form; - } return ( - Date: Thu, 16 May 2019 13:02:07 +0200 Subject: [PATCH 09/13] removed custom form from documentation --- docs/theme-customization.md | 79 ++----------------------------------- 1 file changed, 3 insertions(+), 76 deletions(-) diff --git a/docs/theme-customization.md b/docs/theme-customization.md index 081dc767e5..45846f6926 100644 --- a/docs/theme-customization.md +++ b/docs/theme-customization.md @@ -19,7 +19,7 @@ class Demo extends Component { ``` ### Theme object -The Theme object consists of the properties **widgets**, **templates**, **fields** and **form**. The form merges each prop's value with the default value for it; for example, providing a single widget in **widgets** will merge it with If one of these properties is not specified, the form reverts to the default None are required, although at least one should be provided though. +The Theme object consists of the properties **widgets**, **templates** and **fields**. The form merges each prop's value with the default value for it; for example, providing a single widget in **widgets** will merge it with all default widgets and overrides it if names are equal. If one of these properties is not specified, the form reverts to the default. None are required, although at least one should be provided though. #### widgets and fields **widgets** and **fields** should be in the same format as shown [here](/advanced-customization/#custom-widgets-and-fields). @@ -44,7 +44,7 @@ const ThemeObject = {widgets: myWidgets}; export default ThemeObject; ``` -The above can similarly be done for **fields**. +The above can be similarly done for **fields**. #### templates **templates** should be an object containing template objects which gets spread (using spread operator, like so ``). In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in theme object: @@ -83,78 +83,5 @@ const ThemeObject = { export default ThemeObject; ``` -#### form -Theme can also provide custom form component. - -**Requirments** - - - Custom form have to be valid React component (extends `React.Component` class and implements `render()` method) - - Overrided callback functions *(onSubmit, onError, onChange)*, have to call according callback with appropriate data from `this.props` - - Somewhere in render tree of this component have to be `Form` component - -**Example** - -This can be usefull whenever you need another layer of data handling on top of `Form` component but outside of your app logic. One example could be Bootstrap 4 theme with input like so: -```jsx -function MyBaseInput(props) { - ... - return ( - onBlur(inputProps.id, event.target.value))} - onFocus={onFocus && (event => onFocus(inputProps.id, event.target.value))} - /> - ) -} -``` -Let's say this is override of BaseInput, so any *text*, *textarea*, *number*, ..., inputs looks like this, and we want to have **valid** tag only after form was submitted (otherwise it could show red and green borders around inputs, right after user sees it, which is bad practice), so we have `wasValidated` variable in form context, which gets updated. Below is example of customized ``: -```jsx -import Form from "react-jsonschema-form"; -class Bootstrap4Form extends Component { - constructor(props) { - super(props); - this.state = {wasValidated: false}; - this.onSubmit = this.onSubmit.bind(this); - this.onError = this.onError.bind(this); - } - - onSubmit({...data}) { - this.setState({wasValidated: true}); - if(this.props.onSubmit) { - this.props.onSubmit({...data}); - } - } - - onError({...data}) { - this.setState({wasValidated: true}); - if(this.props.onError) { - this.props.onError({...data}); - } - } - - render() { - const {onSubmit, onError, ...otherProps} = this.props; - return ( - - ) - } -} -``` -and theme would look like this: -```jsx -const Bootstrap4Theme = { - form: Bootstrap4Form, - widgets: {BaseInput: MyBaseInput}, -} -export default Bootstrap4Theme -``` - ### Overriding -As well as theme can override **widgets**, **templates**, **fields** and **form**, you can override **widgets**, **templates**, **fields** just as usual. So user has higher priority than theme and theme higher than default values (**User**>**Theme**>**Defaults**). \ No newline at end of file +As well as theme can override **widgets**, **templates** and **fields**, you can override **widgets**, **templates**, **fields** of theme just as usual. So user has higher priority than theme and theme higher than default values (**User**>**Theme**>**Defaults**). \ No newline at end of file From 3a141fe2a705293657c2e03368bc3538e48fd93b Mon Sep 17 00:00:00 2001 From: mirajp Date: Tue, 21 May 2019 00:31:27 -0700 Subject: [PATCH 10/13] Updated withTheme + docs to show pass-through nature of all the props --- docs/theme-customization.md | 31 +++++++++++++++++++------------ src/withTheme.js | 16 +++++++--------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/theme-customization.md b/docs/theme-customization.md index 45846f6926..2fb600bf04 100644 --- a/docs/theme-customization.md +++ b/docs/theme-customization.md @@ -1,7 +1,7 @@ ## Customizing with other frameworks ### withTheme Higher-Order Component -`withTheme` component provides a way to extend the functionality of react-jsonschema-form by passing in a theme object, which contains an easy way to change the appearance of the form, widgets, templates and fields by passing in theme object. This object is passed as the only parameter like so: `withTheme(ThemeObj)` and it returns component which you use instead of standard `Form` component. +The `withTheme` component provides an easy way to extend the functionality of react-jsonschema-form by passing in a theme object that defines custom/overridden widgets and fields, as well as any of the other possible properties of the standard rjsf `Form` component. This theme-defining object is passed as the only parameter to the HOC (`withTheme(ThemeObj)`), and the HOC will return a themed-component which you use instead of the standard `Form` component. ### Usage @@ -18,10 +18,10 @@ class Demo extends Component { } ``` -### Theme object -The Theme object consists of the properties **widgets**, **templates** and **fields**. The form merges each prop's value with the default value for it; for example, providing a single widget in **widgets** will merge it with all default widgets and overrides it if names are equal. If one of these properties is not specified, the form reverts to the default. None are required, although at least one should be provided though. +### Theme object properties +The Theme object consists of the same properties as the rjsf `Form` component (such as **widgets** and **fields**). The themed-Form component merges together any theme-specific **widgets** and **fields** with the default **widgets** and **fields**. For instance, providing a single widget in **widgets** will merge this widget with all the default widgets of the rjsf `Form` component, but overrides the default if the theme's widget's name matches the default widget's name. Thus, for each default widget or field not specified/overridden, the themed-form will rely on the defaults from the rjsf `Form`. Note that you are not required to pass in either custom **widgets** or **fields** when using the custom-themed HOC component; you can make the essentially redefine the default Form by simply doing `const Form = withTheme({});`. -#### widgets and fields +#### Widgets and fields **widgets** and **fields** should be in the same format as shown [here](/advanced-customization/#custom-widgets-and-fields). Example theme with custom widget: @@ -46,8 +46,8 @@ export default ThemeObject; The above can be similarly done for **fields**. -#### templates -**templates** should be an object containing template objects which gets spread (using spread operator, like so ``). In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in theme object: +#### Templates +Each template should be passed directly into the theme object just as you would into the rjsf Form component. In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in the theme object: ```jsx function MyArrayFieldTemplate(props) { return ( @@ -71,17 +71,24 @@ function MyErrorListTemplate(props) { ); } -const myTemplates = { +const ThemeObject = { ArrayFieldTemplate: MyArrayFieldTemplate, ErrorList: MyErrorListTemplate, + widgets: myWidgets }; +export default ThemeObject; +``` + +### Overriding other Form properties +Just as the theme can override **widgets**, **fields**, any of the field templates, and set default values to properties like **showErrorList**, you can do the same with the instance of the withTheme() Form component. +```jsx const ThemeObject = { - templates: myTemplates + ArrayFieldTemplate: MyArrayFieldTemplate, + fields: myFields, + showErrorList: false, + widgets: myWidgets }; - -export default ThemeObject; ``` -### Overriding -As well as theme can override **widgets**, **templates** and **fields**, you can override **widgets**, **templates**, **fields** of theme just as usual. So user has higher priority than theme and theme higher than default values (**User**>**Theme**>**Defaults**). \ No newline at end of file +Thus, the user has higher priority than the withTheme HOC, and the theme has higher priority than the default values of the rjsf Form component (**User** > **Theme** > **Defaults**). diff --git a/src/withTheme.js b/src/withTheme.js index 2793913149..d3f2d10a1e 100644 --- a/src/withTheme.js +++ b/src/withTheme.js @@ -2,19 +2,18 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import Form from "./"; -function withTheme(data) { +function withTheme(themeProps) { return class extends Component { render() { - let { templates, widgets, fields, ...otherProps } = this.props; - templates = { ...data.templates, ...templates }; - widgets = { ...data.widgets, ...widgets }; - fields = { ...data.fields, ...fields }; + let { fields, widgets, ...directProps } = this.props; + fields = { ...themeProps.fields, ...fields }; + widgets = { ...themeProps.widgets, ...widgets }; return ( ); } @@ -24,7 +23,6 @@ function withTheme(data) { withTheme.propTypes = { widgets: PropTypes.object, fields: PropTypes.object, - templates: PropTypes.object, }; export default withTheme; From 9de51ca0b7322f22cdb59b96970a727f87d03a94 Mon Sep 17 00:00:00 2001 From: Miraj Patel Date: Thu, 23 May 2019 19:22:11 -0700 Subject: [PATCH 11/13] Apply suggestions from code review Co-Authored-By: Ashwin Ramaswami --- docs/theme-customization.md | 4 ++-- mkdocs.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/theme-customization.md b/docs/theme-customization.md index 2fb600bf04..7bdb1f6b75 100644 --- a/docs/theme-customization.md +++ b/docs/theme-customization.md @@ -47,7 +47,7 @@ export default ThemeObject; The above can be similarly done for **fields**. #### Templates -Each template should be passed directly into the theme object just as you would into the rjsf Form component. In [here](/advanced-customization/#array-field-template) and [here](/advanced-customization/#error-list-template) are two examples of custom templates, below is example how to use these two custom templates in the theme object: +Each template should be passed directly into the theme object just as you would into the rjsf Form component. Here is an example of how to use a custom [ArrayFieldTemplate](/advanced-customization/#array-field-template) and [ErrorListTemplate](/advanced-customization/#error-list-template) in the theme object: ```jsx function MyArrayFieldTemplate(props) { return ( @@ -80,7 +80,7 @@ const ThemeObject = { export default ThemeObject; ``` -### Overriding other Form properties +### Overriding other Form props Just as the theme can override **widgets**, **fields**, any of the field templates, and set default values to properties like **showErrorList**, you can do the same with the instance of the withTheme() Form component. ```jsx const ThemeObject = { diff --git a/mkdocs.yml b/mkdocs.yml index f32c08675d..0ef3f96276 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,10 +8,10 @@ nav: - Definitions: definitions.md - Dependencies: dependencies.md - Form Customization: form-customization.md - - Customizing with other frameworks: theme-customization.md + - Theme Customization: theme-customization.md - Validation: validation.md - Playground: https://mozilla-services.github.io/react-jsonschema-form/ markdown_extensions: - toc: - permalink: true \ No newline at end of file + permalink: true From e3f7e175a2ede6a882f67f2ed811df8e327f8d9d Mon Sep 17 00:00:00 2001 From: Miraj Patel Date: Thu, 23 May 2019 22:10:53 -0700 Subject: [PATCH 12/13] Update test/withTheme_test.js Co-Authored-By: Ashwin Ramaswami --- test/withTheme_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/withTheme_test.js b/test/withTheme_test.js index 359ca0505a..9cb8237b22 100644 --- a/test/withTheme_test.js +++ b/test/withTheme_test.js @@ -4,7 +4,7 @@ import React from "react"; import { withTheme } from "../src"; import { createComponent, createSandbox } from "./test_utils"; -describe.only("withTheme", () => { +describe("withTheme", () => { let sandbox; beforeEach(() => { From 12ba00c3857cfcdb940761c3b6e38912cb35bf40 Mon Sep 17 00:00:00 2001 From: mirajp Date: Thu, 23 May 2019 23:10:17 -0700 Subject: [PATCH 13/13] Updated tests --- test/withTheme_test.js | 204 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/test/withTheme_test.js b/test/withTheme_test.js index 9cb8237b22..dc963c4b78 100644 --- a/test/withTheme_test.js +++ b/test/withTheme_test.js @@ -15,8 +15,99 @@ describe("withTheme", () => { sandbox.restore(); }); + describe("With fields", () => { + it("should use the withTheme field", () => { + const fields = { + StringField() { + return
; + }, + }; + const schema = { + type: "object", + properties: { + fieldA: { + type: "string", + }, + fieldB: { + type: "string", + }, + }, + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ fields }), { + schema, + uiSchema, + }); + expect(node.querySelectorAll(".string-field")).to.have.length.of(2); + }); + + it("should use withTheme field and the user defined field", () => { + const themeFields = { + StringField() { + return
; + }, + }; + const userFields = { + NumberField() { + return
; + }, + }; + const schema = { + type: "object", + properties: { + fieldA: { + type: "string", + }, + fieldB: { + type: "number", + }, + }, + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ fields: themeFields }), { + schema, + uiSchema, + fields: userFields, + }); + expect(node.querySelectorAll(".string-field")).to.have.length.of(1); + expect(node.querySelectorAll(".number-field")).to.have.length.of(1); + }); + + it("should use only the user defined field", () => { + const themeFields = { + StringField() { + return
; + }, + }; + const userFields = { + StringField() { + return
; + }, + }; + const schema = { + type: "object", + properties: { + fieldA: { + type: "string", + }, + fieldB: { + type: "string", + }, + }, + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ fields: themeFields }), { + schema, + uiSchema, + fields: userFields, + }); + expect(node.querySelectorAll(".string-field")).to.have.length.of(0); + expect(node.querySelectorAll(".form-control")).to.have.length.of(2); + }); + }); + describe("With widgets", () => { - it("should use the provided widgets", () => { + it("should use the withTheme widget", () => { const widgets = { TextWidget: () =>
, }; @@ -30,5 +121,116 @@ describe("withTheme", () => { }); expect(node.querySelectorAll("#test")).to.have.length.of(1); }); + + it("should use the withTheme widget as well as user defined widget", () => { + const themeWidgets = { + TextWidget: () =>
, + }; + const userWidgets = { + DateWidget: () =>
, + }; + const schema = { + type: "object", + properties: { + fieldA: { + type: "string", + }, + fieldB: { + format: "date", + type: "string", + }, + }, + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ widgets: themeWidgets }), { + schema, + uiSchema, + widgets: userWidgets, + }); + expect(node.querySelectorAll("#test-theme-widget")).to.have.length.of(1); + expect(node.querySelectorAll("#test-user-widget")).to.have.length.of(1); + }); + + it("should use only the user defined widget", () => { + const themeWidgets = { + TextWidget: () =>
, + }; + const userWidgets = { + TextWidget: () =>
, + }; + const schema = { + type: "object", + properties: { + fieldA: { + type: "string", + }, + }, + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ widgets: themeWidgets }), { + schema, + uiSchema, + widgets: userWidgets, + }); + expect(node.querySelectorAll("#test-theme-widget")).to.have.length.of(0); + expect(node.querySelectorAll("#test-user-widget")).to.have.length.of(1); + }); + }); + + describe("With templates", () => { + it("should use the withTheme template", () => { + const themeTemplates = { + FieldTemplate() { + return
; + }, + }; + const schema = { + type: "object", + properties: { + fieldA: { + type: "string", + }, + fieldB: { + type: "string", + }, + }, + }; + const uiSchema = {}; + let { node } = createComponent(withTheme({ ...themeTemplates }), { + schema, + uiSchema, + }); + expect( + node.querySelectorAll(".with-theme-field-template") + ).to.have.length.of(1); + }); + + it("should use only the user defined template", () => { + const themeTemplates = { + FieldTemplate() { + return
; + }, + }; + const userTemplates = { + FieldTemplate() { + return
; + }, + }; + + const schema = { + type: "object", + properties: { foo: { type: "string" }, bar: { type: "string" } }, + }; + let { node } = createComponent(withTheme({ ...themeTemplates }), { + schema, + ...userTemplates, + }); + expect( + node.querySelectorAll(".with-theme-field-template") + ).to.have.length.of(0); + expect(node.querySelectorAll(".user-field-template")).to.have.length.of( + 1 + ); + }); }); });