Extending existing renderer

My Package Versions:
@jsonforms/core”: “2.5.0-alpha.1”,
@jsonforms/material-renderers”: “2.5.0-alpha.1”,

I’m trying to write a renderer for a control that extends the existing renderer while maintaining it’s functionality.
Specifically im trying to extend the MaterialBooleanControl with additional label fields, while using the already implented functionality of disabling/prechecking ControlBoxes.

I’m able to successfully trigger my custom renderer with the tester. I’m also able to output my additional label fields. However, when reusing the MaterialBooleanControl in my custom renderer, something
with the original props breaks, leading to the disabled/data functionality not being passed into the MaterialBooleanControl.

My custom renderer:

import React from ‘react’;
import {
ControlProps,
isBooleanControl,
RankedTester,
rankWith,
} from ‘@jsonforms/core’;
import isEmpty from ‘lodash/isEmpty’;
import { withJsonFormsControlProps } from ‘@jsonforms/react’;
import { FormControlLabel, Hidden } from ‘@material-ui/core’;
import { MuiCheckbox } from ‘@jsonforms/material-renderers’;
import { MaterialBooleanControl } from ‘@jsonforms/material-renderers’

import Grid from ‘@material-ui/core/Grid’;

export const checkBoxWithPricesControl = ({ data,
visible,
label,
id,
enabled,
uischema,
schema,
rootSchema,
handleChange,
errors,
path,
config}: ControlProps) => {
const props = { data,
visible,
label,
id,
enabled,
uischema,
schema,
rootSchema,
handleChange,
errors,
path};
return (


<MaterialBooleanControl { …props}/>

{ props.schema.description }
); }

export const checkBoxWithPricesControlTester: RankedTester = rankWith(
3,
isBooleanControl
);
export default withJsonFormsControlProps(checkBoxWithPricesControl);

My App.tsx:
import React, { Fragment } from ‘react’;
import {
JsonForms,
} from ‘@jsonforms/react’;
import Grid from ‘@material-ui/core/Grid’;
import withStyles, { WithStyles } from ‘@material-ui/core/styles/withStyles’;
import createStyles from ‘@material-ui/core/styles/createStyles’;
import ‘./App.css’;
import schema from ‘./schema.json’;
import uischema from ‘./uischema.json’;
import {
materialCells,
materialRenderers,
} from ‘@jsonforms/material-renderers’;

import MaterialOneOfRadioGroupControl, { materialOneOfRadioGroupControlTester} from’./MaterialOneOfRadioGroupControl’;

import CheckBoxWithPricesControl, { checkBoxWithPricesControlTester} from’./CheckBoxWithPricesControl’;

const styles = createStyles({
container: {
padding: ‘1em’,
},
title: {
textAlign: ‘center’,
padding: ‘0.25em’,
},
dataContent: {
display: ‘flex’,
justifyContent: ‘center’,
borderRadius: ‘0.25em’,
backgroundColor: ‘#cecece’,
},
demoform: {
margin: ‘auto’,
padding: ‘1rem’,
},
});

export interface AppProps extends WithStyles {
}

const data = {
“textBlock” : true,
};

const renderers = [
…materialRenderers,
//register custom renderers
{ tester: materialOneOfRadioGroupControlTester, renderer: MaterialOneOfRadioGroupControl},
{ tester: checkBoxWithPricesControlTester, renderer: CheckBoxWithPricesControl}
];

const config = {
showUnfocusedDescription: true,
};

const App = ({ classes }: AppProps) => {

return (

<Grid
container
justify={‘center’}
spacing={1}
className={classes.container}

); };

export default withStyles(styles)(App);

My uischema.json:
{
“type”: “Categorization”,
“elements”: [
{
“type”: “Category”,
“label”: “Grundpaket”,
“elements”: [
{
“type”: “HorizontalLayout”,
“elements”: [
{
“type”: “Group”,
“label”: “Grundpaket”,
“elements”: [
{
“type”: “Control”,
“scope”: “#/properties/basePackage”,
“options”: {
“format”: “radio”
}

                }
            ]
        }
      ]
    }
  ]
},
{
  "type": "Category",
  "label": "Blöcke",
  "elements": [
    {
      "type": "HorizontalLayout",
      "elements": [
        {
            "type": "Group",
            "label": "Darstellungsblöcke",
            "elements": [
                { 
                    "type": "Control", 
                    "scope": "#/properties/textBlock",
                    "rule": {
                        "effect": "DISABLE",
                        "condition": {
                            "scope": "#/properties/textBlock",
                            "schema":{
                                "const": true
                            }
                        }
                    }
                }
            ]
        }
      ]
    }
  ]
}

],
“options”: {
“variant”: “stepper”,
“showNavButtons”: true
}
}

My schema.json:
{
“type”: “object”,
“definitions”:{
“defaultBlock”: {
“type”: “boolean”,
“price”: “In allen Paketen inbegriffen”
},
“designBlock”:{
“type”: “boolean”,
“price”: 250
},
“functionBlock”:{
“type”: “boolean”,
“price”: 350
}
},
“properties”: {
“basePackage”:{
“type”: “string”,
“title”: “Grundpaket”,
“description”: “Your base package.”,
“oneOf”: [
{ “const”: “light”, “title”: “Light”},
{ “const”: “premium”, “title”: “Premium”},
{ “const”: “extra”, “title”: “Extra”}
]

},
"textBlock":{
    "$ref" : "#/definitions/defaultBlock",
    "title": "TextBlock"
}

}, “required”: [“basePackage”]
}

I know I’m messing something up with either the higher Order Component or passing the ControlProps Interface. The component does render though, but ignores the disabled property and the “textblock:true” from data.
When i rank the custom renderer lower again, the Checkbox renders fine (without my additional Fields of course).

[original thread by Christian Nyffenegger]

Hi! By default we export the already “connected” versions of the controls. However as you’re already using withJsonFormsControlProps yourself you probably want to use the “unwrapped” version of the MaterialBooleanControl. You can import it like this:

import { Unwrapped } from '@jsonforms/material-renderers';
const { MaterialBooleanControl } = Unwrapped;

If this doesn’t solve the problem or if other problems occur, please push your code to a branch so it’s easier to take a look :wink:

[Christian Nyffenegger]

Awesome, that works! Thanks for the fast reply. :slight_smile:

I would prefer to not wrap myself, but I don’t know how I would change the rendered output with the “connected” Component then (or if that is even possible).

I think this could use an example in the docs, because I think there might often be a use-case for only modifying the HTML output, not the functionality of the control itself.

Thanks a lot for this great framework.

I took one closer look at the code: You don’t need to derive the description yourself, it’s already handed over by the withJsonFormsControlProps. Also I would recommend to either not deconstruct the props or at least catch all remaining props so your code stays compatible with MaterialBooleanControl should we change its interface. So the code could roughly look like this (not tested):

import { Unwrapped } from '@jsonforms/material-renderers';
const { MaterialBooleanControl } = Unwrapped;

export const checkBoxWithPricesControl = (props: ControlProps) => {
  return (
    <Grid container>
      <Grid item sm={4}>
        <MaterialBooleanControl {...props} />
      </Grid>
      <Grid item sm={4}>
        <div></div>
      </Grid>
      <Grid item sm={4}>
        <div>{props.description} </div>
      </Grid>
    </Grid>
  );
};

[Christian Nyffenegger]

Yes! That’s much neater (and does work). Those were remnants from me debugging :slight_smile:

I wrote my previous answer before I saw your answer :wink: Some more remarks:

I would prefer to not wrap myself, but I don’t know how I would change the rendered output with the “connected” Component then (or if that is even possible).

If you don’t want to “wrap” your renderer you can simply omit the withJsonFormsControlProps for your component. However in that case you will only get the basic props (uischema, schema, path, enabled, renderers, cells and id). Props like description which are calculated by withJsonFormsControlProps then need to be resolved by yourself, like you did in the original code example. Also then you need to again use the default exported already wrapped MaterialBooleanControl.

I think this could use an example in the docs, because I think there might often be a use-case for only modifying the HTML output, not the functionality of the control itself.

Yes that makes sense. The JSON Forms website is also open source, so if you want to contribute feel free to do so :wink:

Thanks a lot for this great framework.

Thank you!

[Christian Nyffenegger]

I went ahead with your suggestion and wrote some documentation. Hopefully it will be useful to someone :slight_smile: Extending existing controls with additional HTML by freakpants · Pull Request #138 · eclipsesource/jsonforms2-website · GitHub

Hi @christian-nyffenegger(christian-nyffenegger) thank you very much, this is absolutely awesome!