Tooltips on each field?

Hello!

Does JSONForms support tooltips (maybe hover over tooltips)? I see that some fields like string textbox support a description property and it appears as a light grey text below the field. But this it does not appear for arrays.

If there is no built-in way, the long solution I was leaning towards is somehow creating a wrapper for all jsonform components and register them all as custom controls. But this is tedious haha.

Thanks!

[original thread by Ricardo Guntur]

At the moment there is no built-in support for tooltips, so you’ll need to support it via custom renderers. Note that arrays (when rendered as a table) and normal controls use different renderers (cells render table content, renderers everything else), so when you only need tooltips for tables it is enough to customize the cells.

When you want to generically wrap all kinds of renderers (to avoid reregistering all of them) you could register a renderer with a high priority which does the wrapping and then just dispatches again to rest of the renderer set. Just make sure that you don’t include the wrapper renderer again when dispatching as you’ll get an infinite loop.

[Ricardo Guntur]

Thanks, this makes a lot of sense. Can you help clarify what you mean by ‘dispatching again to rest of the renderer set’?

Do you mean, in the custom control, I would somehow dispatch the rest of the renderer set? My custom control looks like this Imgur: The magic of the Internet where I imported MaterialTextControl. But this scenario only wraps that one component.

Hi @ricardo-guntur, I thought of doing it with the help of JsonFormsDispatch:

const WrapperControl = ({ uischema, schema, path }: ControlProps) => {
  return (
    <div /*wrapper*/ >
      <JsonFormsDispatch
        uischema={uischema}
        schema={schema}
        path={path}
        renderers={materialRenderers} /*need to use renderers in which this wrapper is not included*/
      />
    </div>
  );
};

However there is one problem: JsonFormsDispatch currently ignores the renderer attribute and always uses the renderers from the context. I fixed it locally for me and then the approach mentioned above worked. I opened a ticket to implement a proper fix: Make sure that all attributes of JsonFormsDispatch are properly determined · Issue #1529 · eclipsesource/jsonforms · GitHub

Until this is implemented you’ll need to register a wrapper for each control, or copy the dispatching functionality from JSON Forms into your wrapper control and dispatch yourself.

Note that this wrapping approach will also only work if it is fine that your wrapper is around the whole control, e.g. Hidden>FormControl>…. If you want to have the tooltip inside this hierarchy this will not work anyway.

[Ricardo Guntur]

This makes a lot of sense. I have to upgrade our jsonforms version to implement this. We’re a bit behind on 2.2.3. After upgrading I will attempt to copy the dispatching functionality to fix the bug and implement the wrapper! I’ll come back with an update eventually. And I’ll convince my manager to get your professional support if we do even more wacky stuff. Thanks again!

[Ricardo Guntur]

Hello @sdirix!

It’s been a while but I’m back with an update. I see that the fix you mentioned regarding JsonForms ignoring the renderer attribute was merged and released in the latest Alpha 2.4.0-alpha.3.

I’ve attempted to do the above and it seems my wrapping content only appears once above the entire form, as opposed to each control field.

Here is how I render jsonforms initially on the page.

    return (
        <JsonForms
            data={data}
            schema={schema}
            uischema={uischema}
            renderers={[
                {tester: WrapperControlTester, renderer: WrapperControl}
                ]
            }
            cells={materialCells}
            onChange={({ errors, data }) => updateData(errors, data)}
            ajv={ajv}
        />
    )

And here is the WrapperControl

const WrapperControl = (props) => {
    const {uischema, schema, path} = props
    console.log(props)
    return (
        <div>
            <div> wrapper stuff </div>
            <JsonForms
            uischema={uischema}
            schema={schema}
            path={path}
            renderers={[
                ...materialRenderers,
            ]}/>
        </div>
    )
}

I may have misunderstood but I thought that this nested approach would mean that jsonforms would attempt to render each field in my schema through the wrapper first, then attempt to render the actual field after. In the end it does load but ‘wrapper stuff’ appears at the top of the form, as opposed to above each field.

Thanks!

Guessing from your description I think the main problem lies with the tester.

For this to work the tester should only match to Controls. If it also matches to layouts (for example VerticalLayout), then the wrapper will apply once to the layout, the rendered layout will receive the renderers without the wrapper and the following controls (e.g. inputs) will be rendered without wrapping. Instead the layout should be rendered without wrapping (no tooltip needed here), then for each control the wrapper will be called which again renders the normal control.

Also the wrapper control should be handled differently:

  • JsonForms are not meant to be nested, as this component does a lot of initialization and setup which is only needed once, instead JsonFormsDispatch should be used within JsonForms.

  • In the WrapperControl I would just take all the props you’re given and pass them down, except for the renderers attribute, i.e.

const WrapperControl = (props) => (
<div>
  <div> wrapper stuff </div>
  <JsonFormsDispatch
    {...props}
    renderers={materialRenderers}
  />
</div>);

Edit: I previously advised to wrap the wrapper with withJsonFormsControlProps. Thinking about it this is not necessary for this use case as each control later rendered is already wrapped with it. However some form of wrapper still could make sense, depending on the needs of the tooltip renderer.

[Ricardo Guntur]

You are right. I managed to get it working by adjusting the tester to use isControl instead.

Looks like this with tool tips wrapping some text and bool fields.

I was unable to use JsonFormsDispatch in the WrapperControl because it was causing an infinite render, though it might be my fault. The JsonForms component does not cause an infinite re-render. From the docs I thought JsonFormsDispatch was for usage with redux.

We actually implement the isolated JsonForms and use the onChange prop to call a function that we pass in from outside this entire component. Because our save functionality is else where. It looks like this, I hope I’m explaining it right.

const updateData = (errors, data) => {
        onJsonFormChange(data)
    }

    const ajv = createAjv({useDefaults: true})
    return (
        <JsonForms
            data={data}
            schema={schema}
            uischema={uischema}
            renderers={[
                ...materialRenderers,
                {tester: rankWith(Number.MAX_VALUE, isControl), renderer: WrapperControl},
            ]}
            cells={materialCells}
            onChange={({errors, data}) => updateData(errors, data)}
            onJsonFormChange={onJsonFormChange}
            ajv={ajv}
        />
    )

Where onJsonFormChange is passed in and sets a state outside this component.

There may be some limitations here though:

  • I’m not able to see custom props or props like data or onChange in the WrapperControl. I suspect JsonForm renders the WrapperControl and decides to only pass specific props like schema or uischema.
    Let me investigate this, because I definitely pass data to my other custom controls.

So my question is, am I able to pass custom props into the WrapperControl? If not, I may need to think of another solution because the data is not updating with this setup.

update:
Wrapping the WrapperControl with withJsonFormsControlProp allows me to see the other props. Currently debugging why the inner JsonForm does not display values or allow me to change the data when typing.

JsonFormsDispatch vs JsonForms
JsonFormsDispatch is the component we use whenever we want the render engine to determine and execute the next renderer, so it’s definitely the one to use. JsonForms does nothing besides initializing the React context and then also dispatching to JsonFormsDispatch. I didn’t try yet what happens when you start to nest JsonForms components but I can very well imagine weird side effects. In the docs JsonFormsDispatch is exposed in the Redux use case as we don’t provide a convenience wrapper (for example JsonFormsRedux) for Redux. So you definitely need to use JsonFormsDispatch. If you can push your code (or example code) somewhere in an easily accessibly way I’ll take a look why the infinite rendering occurs. You could also try to use the ResolvedJsonFormsDispatch and see if that works better.

Props for WrapperControl
When you don’t wrap WrapperControl you’ll only get the props which are immediately passed down by the parent component, e.g. uischema, schema, path etc. Our provided wrappers (e.g. withJsonFormsControlProps) just access the JSON Forms React context and use the information stored there together with the passed down props to give specialized props to the wrapped component. You could also do all of this manually in WrapperControl (you can access the context with our custom useJsonForms() hook) or write your own wrapper. You can take a look at mapStateToControlProps which is called by withJsonFormsControlProps. Note that you don’t need to explicitly handle the onChange in your custom renderer as this is already taken care of at that point.

Other

  • I would like to suggest to create ajv once outside of your component and pass that in instead of creating a new instance with each render

  • Did you customize JsonForms? Otherwise onJsonFormChange={onJsonFormChange} will not have an effect and just get lost.

Hi @discobot / Ricardo Guntur , are you able to get data for your wrapper control. I am trying to use ResolvedJsonFormsDispatch for my List wrapper control but I could not get data show up.

I have this other post for details.

Any pointer would help.

Regards,
Kushal

Hi! To find out what I can do, say @discobot display help.