Custom Renderer - Why to put Tester and Renderer in separate files?

Hi,
sorry for a stupid question, but I always stumble again and again over the problem, that in Javascript code often is simplified to the point of unrecognizability - I am an old JEE developer :wink:

I try to implement a custom renderer and take the Ranking example as a base. To keep my code more compact I put the React FunctionalComponent and the Control element into one single file (module).

But I was not able to put also the Tester into my module and out of a separate file.

import { rankWith, scopeEndsWith } from '@jsonforms/core';
export default rankWith(3, scopeEndsWith('eventdefinitions'));

Can someone explain me how I can define a export (not a default) in my custom Renderer module that provides an equal tester without the need to put the tester into a separate file. Or is there some deeper meaning to it?

I am really sorry about this question, but I can’t find any information about the general functionality of the Tester. I can see in the sources that there is some other layer providing the Schema and UISchema which may be useful to implement a more intelligent testing.

===
Ralph

While searching around in the vanilla array renderer implementation, I found the solution finally by myself :slight_smile:

You can of course define your Renderer and Tester in one module (single file) with the following code setup:

interface MyRendererProps {
 ....
}

interface MyRendererControlProps {
 ....
}

const  MyRendererComponent: React.FC< MyRendererProps> = ({ value, updateValue }: any) => (
    <div ....> 
   ..........
    </div>
);

const MyRendererControl = ({ data, handleChange, path }: MyRendererControlProps) => (
  <MyRendererComponent
    value={data}
    updateValue={(newValue: number) => handleChange(path, newValue)}
  />
);

// Export the renderer and the tester
export const MyRendererTester: RankedTester = rankWith(
  7,
  scopeEndsWith('.......')
);

export const MyRenderer: any = withJsonFormsControlProps(MyRendererControl);

Now you can import your custom renderer in any other module:

import {MyRendererTester,MyRenderer} from './myRenderer';

and define the tester and renderer in your JsonForms setup:

........
	const myRenderers = [
		...vanillaRenderers,
		//register custom renderers 
		{ tester: MyRendererTester, renderer: MyRenderer}
	];
	ReactDOM.render(
		<JsonForms
			data={.....}
			schema={......}
			uischema={.....}
			cells={vanillaCells}
			renderers={myRenderers}
			onChange={........}
		/>,
		this.bodyDiv
	);

It may be, that this solution is obvious to many of you, but it helps me a lot in understanding code, if I combine the behavior of one custom renderer in one single module.

Hi @rsoika,

yeah the code base is sadly a bit all over the place on where each renderer and tester is located.

Instead of exporting the renderer and tester separately (or maybe in addition to it) you could also export the whole “entry”, i.e. the { renderer, tester} object so that you don’t need to construct that in place.

Ah! Yes I understand. Doing this makes the module again more compact and clear. My module is now only exporting the renderer const like this:

......
export const BPMNEventDefinitionRenderer: any = { 
  tester: rankWith(7,scopeEndsWith('conditions')), 
  renderer: withJsonFormsControlProps(BPMNEventDefinitionControl)
};

The react FunctionComponent and the Control are still private . I will see if this works until my final implementation is finished…

Instead of any you can use JsonFormsRendererRegistryEntry. For application code keeping the renderer and tester private is totally fine. Should you work on a library, e.g. a renderer set shared over multiple applications, you probably want to export the unwrapped version of the renderer too so it can be more easily reused if the need arises.