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

"ui:template" schema option #1010

Closed
1 of 2 tasks
loganvolkers opened this issue Aug 21, 2018 · 11 comments · Fixed by #1152
Closed
1 of 2 tasks

"ui:template" schema option #1010

loganvolkers opened this issue Aug 21, 2018 · 11 comments · Fixed by #1152

Comments

@loganvolkers
Copy link
Contributor

Prerequisites

  • I have read the documentation;
  • In the case of a bug report, I understand that providing a SSCCE example is tremendously useful to the maintainers.

Description

For more theming control a "ui:template" option would be helpful. From what I can tell this would be a fairly simple matter to integrate three places:

  • SchemaField
  • ObjectField
  • ArrayField

This would build on the work in #304.

Expected behavior

People could use custom templates to customize layout. This would be the most pertinent for objects and arrays, but can only be useful for leaf nodes too.

const uiSchema = {
  "ui:template": GroupedObjectTemplate,
  title: {
    "ui:template": InlineField
  }
  done: {
    "ui:template": ErrorsInTooltip
  },
  comments: {
    "ui:template": InlineArray
  }
};

Alternative 1 - External plugin

Currently as a workaround this behaviour can be (almost) achieved externally. The biggest blocker to making it easier to achieve externally is that the default template objects are not exported.

import React from "react";
// TODO: Would be useful to 
import {
  DefaultObjectFieldTemplate,
  DefaultFieldTemplate,
  DefaultArrayItem
} from "./DefaultTemplates";

export function FieldTemplate(props) {
  const Template = props.uiSchema["ui:template"];
  if (
    Template &&
    // TODO: LV: Seems strange that this is required to be plugged in... oh well
    props.schema.type !== "object" &&
    props.schema.type !== "array"
  ) {
    return <Template {...props} />;
  } else {
    return <DefaultFieldTemplate {...props} />;
  }
}

export const ObjectFieldTemplate = defaultOrComponent(
  DefaultObjectFieldTemplate
);
export const ArrayFieldTemplate = defaultOrComponent(DefaultArrayItem);

function defaultOrComponent(DefaultTemplate) {
  return function(props) {
    const Template = props.uiSchema["ui:template"];
    if (Template) {
      console.error("Can't render", Template, props);
      return <Template {...props} />;
    } else {
      return <DefaultTemplate {...props} />;
    }
  };
}

And then use that on your Form

  import * as UiTemplate from "...";
  
  const fm = <Form
    schema={schema}
    uiSchema={uiSchema}
    onChange={log("changed")}
    onSubmit={log("submitted")}
    onError={log("errors")}
    transformErrors={log("transformErrors")}
    liveValidate={true}
    formContext={{
      templates: Templates.GroupTemplates
    }}
    {...UiTemplate}
  >

Alternative 2 - "ui:field"

"ui:field" is very powerful, but it requires too much re-implementation of core behaviour to get right, particularly for things like arrays and objects.

Recommendation

I would recommend tackling this in phases, getting simpler functionality merged in earlier, and complex functionality merged in later.

Phase 1 allow templates as objects

  • Before: const Template = registry.ObjectFieldTemplate || DefaultObjectFieldTemplate;
  • After: const Template = uiSchema["ui:template"] || registry.ObjectFieldTemplate || DefaultObjectFieldTemplate;

Phase 2 allow templates as strings, add templates to registry, allow custom templates map, add default templates to default registry

Phase 3 themes for other ui libraries

Resolution

If the maintainers of this repository are on-board, then I can work on a pull request.

@MatejBransky
Copy link

MatejBransky commented Aug 24, 2018

A similar idea is used here.

I wanted to separate the core from the UI which means that I had to separate templates from fields and these templates together with widgets are stored in a separate theme package so you can build completely custom theme and the original one will not be included. 🙂

Related issue: #899 (comment)

@loganvolkers
Copy link
Contributor Author

loganvolkers commented Aug 24, 2018 via email

@MatejBransky
Copy link

MatejBransky commented Aug 24, 2018

I don't know.. @glasserc didn't response yet.

But I'm afraid that this is a lot of changes so it's not so easy to just merge completely restructured library (my version is split into multiple packages: core, bootstrap and in the future there will be boilerplate package for third party themes so you can just use copy & paste and rewrite only the theme specific parts).

But you can use it today. It's just published under @react-schema-form/bootstrap (or if you want to build a completely custom theme then you can use directly @react-schema-form/core, here is guide).

If you find any issues, don't hesitate and tell me about it.

If you want to know what else I changed then look into the merged PRs (the main is rewrite).

@loganvolkers
Copy link
Contributor Author

loganvolkers commented Aug 24, 2018 via email

@MatejBransky
Copy link

MatejBransky commented Aug 24, 2018

Ok, but I need to update readme because there are some differences like replaced Form.props.FieldTemplate with Form.props.templates.FieldTemplate and etc.

The problem is that readme is so looong. 😄

Oh and btw I've changed my mind about ui:FieldTemplate because it's not so useful. I'm just using something like this:

./myTemplates/MyFieldTemplate.js

import { templates } from '@react-schema-form/bootstrap'

const FieldTemplate = props => {
  if (myCondition) {
    return <MyFieldTemplate {...props} />;
  }
  return <templates.FieldTemplate {...props} />;
};

export default FieldTemplate;

(the same applies to ArrayFieldTemplate and ObjectFieldTemplate)

./MyForm.js

import Form from '@react-schema-form/bootstrap';

import myTemplates from './myTemplates';

export default props => <Form templates={myTemplates} {...props} />;

@MatejBransky
Copy link

MatejBransky commented Aug 25, 2018

I think I've found the way how to keep codebase the same and still support everything what I've done. 🙂

Form with Bootstrap:

import Form from 'react-jsonschema-form';

Form with a custom theme:

import withTheme from 'react-jsonschema-form/lib/components/withTheme';

import templates from './my-theme/templates';
import widgets from './my-theme/widgets';

const Form = withTheme('MyTheme', { templates, widgets });

But I need some time for it.

Edit: I have almost a finished PR with the change: "separating templates and ability to use a completely custom UI components without default Bootstrap components". I only need to update docs in readme.

@loganvolkers
Copy link
Contributor Author

Nice, looks promising. Now we wait for contributors...

@loganvolkers
Copy link
Contributor Author

@epicfaace I think this feature would have a small API surface area change, but also give a lot of power for theming.

In particular, this is useful for doing object layouts and array layouts from left right, or with wrapper elements, or custom styles.

image

@loganvolkers loganvolkers mentioned this issue Jan 22, 2019
3 tasks
@epicfaace
Copy link
Member

@loganvolkers Yes, ui:template is a great idea and it also wouldn't break backwards compatibility so it would be easier to merge than @MatejBransky 's PR.

@loganvolkers
Copy link
Contributor Author

@epicfaace Great I'll work on getting something wired up.

@epicfaace
Copy link
Member

Do you think it would be useful to have a ui:TitleField and ui:DescriptionField? This would be for the use case in which one wants to override a title / description field for a specific field, but doesn't want to necessarily override the entire field template. I'm thinking specifically of #1146 .

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 a pull request may close this issue.

3 participants