Custom renderer for top level object

Hello everyone.
I have a special requirement. In my application, all properties needs to be an identifier.
I have a this schema:

  {
    "type": "object",
    "properties": {
        "id1": {
            "type": "string",
            "oneOf": [
                {
                    "const": "1",
                    "title": "I love it"
                },
                {
                    "const": "2",
                    "title": "I dont like it"
                }
            ]
        },
        "id2": {
            "type": "string",
            "oneOf": [
                {
                    "const": "1",
                    "title": "I love it"
                },
                {
                    "const": "2",
                    "title": "I dont like it"
                }
            ]
        },
        "id98": {
            "type": "string",
            "oneOf": [
                {
                    "const": "1",
                    "title": "I love it"
                },
                {
                    "const": "2",
                    "title": "I dont like it"
                }
            ]
        }
    }
  }

I want to achieve a table, where each header is the title of the const, the associated cell is a checkbox, with a special readonly column where we would put the property name/label. Something like this:

Of course I need a custom renderer. The tester would be “all properties are string, oneOf with same const”. Unfortunately, it’s imposible for me to create such a tester, because the tester only test each property separetely. I would need to make a renderer for the whole object. But is it really possible?

I think it would need to be a Layout, but it doesn’t seem to be the way to go

I ended up making a custom renderer for the oneOf element that is a TableRow, with an option to hide or show the header.

import React, { useMemo } from 'react';
import {
  ControlProps,
  isOneOfControl,
  RankedTester,
  rankWith,
  JsonSchema,
  and,
  optionIs,
} from '@jsonforms/core';
import { withJsonFormsControlProps } from '@jsonforms/react';
import { Box, Checkbox, TableCell, TableRow, Typography } from '@mui/material';

/**
 * Tester for the OneOfRow renderer.
 * Checks if the schema is a oneOf with string type and the UI schema has format: "checkbox"
 */
export const oneOfRowTester: RankedTester = rankWith(
  10,
  and(
    isOneOfControl,
    optionIs('format', 'checkbox')
  )
);

/**
 * A custom renderer that displays oneOf options as checkboxes in a table row.
 */
const OneOfRow = (props: ControlProps) => {
  const {
    data,
    handleChange,
    path,
    schema,
    uischema,
    visible,
    errors,
    label
  } = props;

  // Extract options from the schema
  const options = useMemo(() => {
    if (schema.oneOf && Array.isArray(schema.oneOf)) {
      return schema.oneOf.map((option: JsonSchema) => ({
        const: option.const,
        title: option.title || String(option.const)
      }));
    }
    return [];
  }, [schema]);

  // Check if header should be shown
  const showHeader = uischema.options?.showHeader !== false;

  if (!visible) {
    return null;
  }

  return (
    <TableRow>
      {/* Label column if provided */}
      {label && (
        <TableCell>
          <Typography variant="body1">{label}</Typography>
          {errors && (
            <Typography color="error" variant="caption">
              {errors}
            </Typography>
          )}
        </TableCell>
      )}

      {/* Render each option as a checkbox in its own cell */}
      {options.map((option) => (
        <TableCell key={String(option.const)}>
          <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
            <Typography 
              variant="body2" 
              sx={{ visibility: showHeader ? 'visible' : 'hidden' }}
            >
              {option.title}
            </Typography>
            <Checkbox
              checked={data === option.const}
              onChange={() => handleChange(path, option.const)}
              color="primary"
            />
          </Box>
        </TableCell>
      ))}
    </TableRow>
  );
};

export default withJsonFormsControlProps(OneOfRow);

This way, I can stack them in a vertical layout and have the desired effect.

1 Like