Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding the default fields/widgets #371

Merged
merged 32 commits into from
Nov 7, 2016

Conversation

olzraiti
Copy link
Contributor

@olzraiti olzraiti commented Nov 2, 2016

Reasons for making this change

This pull request is conflicting with #363 and #366. There is some heated discussion about the technical details in the comments of those pull requests. I'm offering this pull request because I had the code changes lying around anyway (as I have mentioned in #310). My intention is not to step on anyones toes - I'm merely offering a solution or at least some insight to the issue.

The reason why I have sitted on this for so long time is because I was waiting for input on the issues I have raised (#354, #310).

This approach has the benefit that we aren't introducing any new techniques or new API designs to solve the issue. All fields and widgets are simply overridable through the fields and widgets props.

I didn't add any tests or update the documentation since this pull request is conflicting.

Checklist

  • I'm updating documentation
    • I've checked the rendering of the Markdown text I've added
    • If I'm adding a new section, I've updated the Table of Content
  • I'm adding or updating code
    • I've added and/or updated tests
    • I've updated docs if needed
  • I'm adding a new feature
    • I've updated the playground with an example use of the feature

Copy link
Collaborator

@n1k0 n1k0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the simplicity of your approach. I'd like to hear from @frassinier and @jmfrancois about it; do you guys feel like this could address your own use cases?

SchemaField: _SchemaField,
TitleField: _TitleField,
DescriptionField: _DescriptionField,
}, this.props.fields);
Copy link
Collaborator

@n1k0 n1k0 Nov 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were kept for backward compatibility purpose, but I'm glad seeing them going away. The change should be documented though (in the next release changelog).

StringField: require("./components/fields/StringField").default,
NumberField: require("./components/fields/NumberField").default,
TitleField: require("./components/fields/TitleField").default,
DescriptionField: require("./components/fields/DescriptionField").default,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For legibility, we could create some load helper, eg.

const load = comp => require(`./components/${comp}`).default;
const fields = {
  ArrayField: load("fields/ArrayField"),
  // ...
};

@frassinier
Copy link
Contributor

@olzraiti Could you provide sample of customization please?

@olzraiti
Copy link
Contributor Author

olzraiti commented Nov 4, 2016

@frassinier for example if you would like to add a material theme, you would just override the specific widgets like so:

Example 1: Themes

If you need different themes, you could easily wrap your form like so:

function ThemedForm(props) {
    const themes = {
        material: {
            widgets: {
                CheckboxWidget: CustomMaterialCheckbox
            }
        },
        someOtherTheme: {
            ...what ever you would like to override
        }
    }

    <Form
        {...themes[props.theme]} 
    />
}

Now you have a form component which you can render with different themes:

<ThemedForm theme="material" />

Example 2: non orderable arrays as default

import ArrayField from 'react-jsonschema-form/lib/components/fields/ArrayField';

function NonOrderableArrayField(props) {
    return (
        <ArrayField 
                   {...props} 
                   uiSchema={
                       ...props.uiSchema, 
                       "ui:options": {...props.uiSchema["ui:options"], orderable: false} 
        />
    );  
}

<Form fields={{
  ArrayField: NonOrderableArrayField
}} />

@frassinier
Copy link
Contributor

Love it, I will close my PR and help you by providing material-ui theme if you want?

@olzraiti
Copy link
Contributor Author

olzraiti commented Nov 4, 2016

I'm glad we came to an agreement so fast, thanks for being so flexible. :)

It's up to @n1k0 if this library should provide themes. I can think of two ways this library could go:

  1. Provide no themes at all (not even the current bootstrap themes). Let the users handle the themes themselves.
  2. Provide pluggable themes (I'd like to see the bootstrap theme also as a plugin)

@olzraiti
Copy link
Contributor Author

olzraiti commented Nov 4, 2016

I didn't add many new tests or extend the documentation, since we are mostly removing a special case instead of adding new features (SOME of the fields/widgets have been overridable before this pull request, but not all. Now all of them are overridable). Maybe the documentation for a custom SchemaField could be merged to the added documentation chapter? Though having a specific chapter for SchemaField makes sense, since it's a major building block.

@frassinier
Copy link
Contributor

Good job @olzraiti ! I will work on a first theme impl ASAP :)

@@ -358,6 +358,7 @@ class App extends Component {
formData={formData}
onChange={this.onFormDataChange}
fields={{geo: GeoPosition}}
widgets={{AltDateTimeWidget: () => <div id="#custom" >moi</div>}}
Copy link
Contributor

@frassinier frassinier Nov 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really useful? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha wow... sorry for that :) It's "hi" in finnish.

@frassinier
Copy link
Contributor

Hey @olzraiti, it works well! Here is an example. cc @n1k0

Copy link
Collaborator

@n1k0 n1k0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks super good. A few nits and we're good to go, thank you 👍

@@ -897,6 +898,32 @@ If you're curious how this could ever be useful, have a look at the [Kinto formb

Props passed to a custom SchemaField are the same as [the ones passed to a custom field](#field-props).

### Customizing any fields or widgets
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about Customizing default fields and widgets?

uiSchema={uiSchema}
formData={formData}
widgets={widgets} />
), document.getElementById("app"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may look super obvious to seasonned react developers, but maybe we should augment this section with an example of a reusable customized form class, eg.

const fields = {StringField: CustomString};
const widgets = {CheckboxWidget: CustomCheckbox};

function MyForm(props) {
  return <Form fields={customFields} widgets={customWidgets} {...props} />;
}

render((
  <MyForm schema={schema}
    uiSchema={uiSchema}
    formData={formData} />
), document.getElementById("app"));

Copy link
Contributor

@frassinier frassinier Nov 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo but +1

const customFields = {StringField: CustomString};
const customWidgets = {CheckboxWidget: CustomCheckbox};

function MyForm(props) {
  return <Form fields={customFields} widgets={customWidgets} {...props} />;
}

@@ -31,7 +26,7 @@ function getFieldComponent(schema, uiSchema, fields) {
if (typeof field === "string" && field in fields) {
return fields[field];
}
return COMPONENT_TYPES[schema.type] || UnsupportedField;
return (COMPONENT_TYPES[schema.type] ? fields[COMPONENT_TYPES[schema.type]] : UnsupportedField) || UnsupportedField;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

const componentName = COMPONENT_TYPES[schema.type];
return componentName in fields ? fields[componentName] : UnsupportedField;

@olzraiti
Copy link
Contributor Author

olzraiti commented Nov 7, 2016

Closes #272, closes #310, closes #363.

That's a neat example @frassinier, thanks for showing the potential this pull request brings!

@n1k0
Copy link
Collaborator

n1k0 commented Nov 7, 2016

Thanks for this great collaborative effort!

@n1k0 n1k0 merged commit f24a728 into rjsf-team:master Nov 7, 2016
n1k0 added a commit that referenced this pull request Nov 9, 2016
This release has been made possible by the combined work of @olzraiti, @maartenth, @vinhlh, @tiemevanveen , @dehli, @enjoylife, @pabloen, @israelshirk, @sjhewitt and @rom10. Thank you folks!

Breaking changes
----------------

Support for passing `DescriptionField`, `TitleField` and `SchemaField` as `Form` props as been dropped in this release. You now have to always pass them through the `fields` prop.

Deprecations
------------

* Leverage `ui:options` everywhere (fix #370) (#378)

There's now a unique recommended way to specify options for widgets in uiSchema, which is the `ui:options` directive. Previous ways are still supported, but you'll get a warning in the console if you use them.

New features
------------

* Allow overriding the default fields/widgets (#371)
* Pass data to `FieldTemplate` as strings instead of as React components (#341)
* Pass `schema` & `uiSchema` to the field template component (#379)
* Add `ui:order` wildcard feature and improve error messages (#368)
* Add support for widget autofocus (#288)
* Array field optional sortability (#345)
* Radio widget support for integers and numbers (#325)
* Add support for inline radios and checkboxes. (fix #346) (#348)
* Added ref to `FileWidget`. (#355)
* Added `removable` and `addable` options for array items (#373)
* Enable Windows development (#320)

Enhancements and bugfixes
-------------------------

* Fix `minimum/maximum==0` for `UpDownWidget` and `RangeWidget` (#344)
* Handle numbers requiring trailing zeros with more grace (#334)
* Pass empty title to `TitleField` instead of name (#311)
* `asNumber`: return `undefined` when value is empty string (#369)
* Use [glyphicons](http://getbootstrap.com/components/#glyphicons) for buttons by default. (fix #337) (#375)
* Support old versions of React (#312)
@n1k0
Copy link
Collaborator

n1k0 commented Nov 9, 2016

Released in v0.41.0.

@AMongeEntytle
Copy link

Great job guys ! Thank you for all the effort !

@herzaso
Copy link

herzaso commented Aug 7, 2017

I might have missed something but how do I change the default "number" input to an "updown" widget?
Obviously, I need the TextWidget for text inputs...

For example, I want the following schema to produce an "updown" widget without providing the "ui:schema":

schema: {
	title: "Registration",
	type: "object",
	properties: {
		quantity: {type: "integer", title: "Quantity", default: 4}
	}
}

Also, why is the default widget for numbers a TextWidget and not something more numbers oriented?

@Natim
Copy link
Contributor

Natim commented Aug 7, 2017

why is the default widget for numbers a TextWidget and not something more numbers oriented?

I guess because the TextWidget is the best compromise when you have no idea what kind of number one is talking about. For instance up and down button for a timestamp doesn't make any sense.

@Natim
Copy link
Contributor

Natim commented Aug 7, 2017

@herzaso The documentation for custom widget is here: https://github.com/mozilla-services/react-jsonschema-form#custom-widget-components

There apparently are some buildin widget for numbers: https://github.com/mozilla-services/react-jsonschema-form#for-number-and-integer-fields

const uiSchema = {
  quantity: {"ui:widget": "updown"}
};

@herzaso
Copy link

herzaso commented Aug 7, 2017

@Natim , thanks for your comments.

IMO, a better compromise would be to use a field that relates to a number and not to a string since I don't think there would be many real-world situations in which a user would want to have a text input for numbers (I'm not sure that showing a timestamp as is, and not as a date qualifies as a real-world situation). Either way, if the user has the ability to change the default widget, he could do so.

Regarding my main question, I'm looking for a way to change the default widget since my form would be dynamic and I wouldn't want to loop over it. Since your answer refers to a specific field (quantity), I'm not sure that will yield the required result. In my scenario, all number fields should be rendered as a number input field.

In that respect, the first link you provided (the uiSchema function) looks interesting but I do have 2 concerns:

  1. I'll need a way to return the default widget (for all non-numeric fields)
  2. I prefer not to implement the entire input myself as it wouldn't be future-proof. I just want to change the input type to number

@Natim
Copy link
Contributor

Natim commented Aug 7, 2017

I guess this pull-request allow you to do just that :) https://github.com/mozilla-services/react-jsonschema-form#customizing-the-default-fields-and-widgets

@herzaso
Copy link

herzaso commented Aug 7, 2017

Actually, I'm not sure it does as it rely on the default widget to replace and in my situation it will be TextWidget (and I don't want to replace the TextWidget).
I need some mapping between a property type (e.g. "number" / "integer") and a widget (UpDownWidget).
Ideally, that would be a mapping object (e.g. {"number": UpDownWidget}) and not a function replacing the whole implementation...

Update: I see that it relates to #637 although the actual behavior described there does not work for me (widget={{integer: UpDownWidget}})

@annjawn
Copy link

annjawn commented Feb 21, 2020

Hey everyone, I am really struggling to find out how to override widgets globally. We have multiple json schema templates and customizing them individually is going to be painful. Just like MaterialUI we would like to use AntD components but I am not sure how to globally override Text inputs, checkboxes, radio buttons, select's etc. any pointer here would be appreciated. I have pretty much same requirement as @herzaso.

@epicfaace
Copy link
Member

epicfaace commented Feb 25, 2020

@annjawn you'd have to customize each component individually. Eventually if people are interested they could contribute an antd theme (like for material ui) which would take a lot of work to initially make, but would then be easy for others to use.

To globally override things, check out FieldTemplate in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants