Skip to content

Theming

Charly POLY edited this page Jun 6, 2018 · 5 revisions

One particularity of react-jsonschema-form is to separate "data structure" from "ui structure".

<ApolloForm> tries to simplify the logic around this by enabling "theming".

When configuring a Form component, you can pass a optional theme argument:

interface ApolloFormConfigureTheme {
    templates?: ApolloFormTheme['templates'];
    widgets?: ApolloFormTheme['widgets'];
    fields?: ApolloFormTheme['fields'];
    renderers?: Partial<ApolloFormTheme['renderers']>;
}

(if fields, templates, widgets are unknown to you, please read Understanding "react jsonschema form" in 2 minutes )

Theme allows you to override rendering of core components: fields, templates and renderers. Or add some custom widgets for complex properties or types.


Build our custom theme with Semantic UI

You will find all default fields, templates and widgets react-jsonschema-form repo

With no theming, our "Todo Form" looks like this:

https://s3.eu-west-2.amazonaws.com/github-oss/react-apollo-form/getting-started-todo-form.png

Our Semantic UI theme:

import { Button, Input, Checkbox, Header, Form, Message } from 'semantic-ui-react';

const ErrorList: ErrorListComponent = p => (
    <Message
        error={true}
        visible={true}
        header="There was some errors"
        list={p.errors.map(e => e.message)}
    />
);

const theme: ApolloFormConfigureTheme = {
    templates: {
        FieldTemplate: props => {
            const { classNames, help, description, errors, children, rawErrors, label } = props;
            return (
                <Form.Field>
                    <label>{label}{props.required && '*'}</label>
                    {children}
                    <span>{description}</span>
                </Form.Field>
            );
        },
        ObjectFieldTemplate: props => {
            return (
                <div>
                    {props.properties.map(p => p.content)}
                </div>
            );
        }
    },
    fields: {
        StringField: (p: FieldProps) => (
            <Input value={p.formData} onChange={
                (e: React.SyntheticEvent<HTMLInputElement>) => p.onChange(e.currentTarget.value)
            } />
        ),
        BooleanField: (p: FieldProps) => (
            <Checkbox label={p.title} checked={p.formData} onChange={
                (e: React.SyntheticEvent<HTMLInputElement>, data: object) => {
                    // tslint:disable-next-line:no-any
                    p.onChange((data as any).checked);
                }
            } />
        )
    },
    renderers: {
        saveButton: p => (
            <Button onClick={p.save} primary={true}>
                Save
            </Button>
        ),
        cancelButton: p => (
            <Button onClick={p.cancel}>
                Cancel
            </Button>
        ),
        header: p => (
            <Header as="h1">{p.title}</Header>
        )
    }
};

Just pass the theme to configure and pass ErrorList to your form ui={} prop.

Now, our form is shiny:

https://s3.eu-west-2.amazonaws.com/github-oss/react-apollo-form/theming-semantic-ui.png


⚠️ Warning, note on Field overrides ⚠️

As seen in Understanding "react jsonschema form" in 2 minutes ), fields are responsible of instanciating a widget component if ui:widget is present in the field uiSchema.

If you define a new Field component, remember to check if a ui:widget is specified.

If so, just fallback to the original field component that will instanciate the widget properly.

Example

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

export const MyStringField = (props: FieldProps) => {
    const uiSchema = props.uiSchema;
    return (
        // render original StringField if 'ui:widget'
        uiSchema && uiSchema['ui:widget'] ?
            <StringField {...props} /> :
            <MyInput
                type="text"
                value={props.formData}
                name={props.name}
                onChange={props.onChange}
            />
    );
};