I have reviewed similar topics and have not found the answer I am looking for.
I am struggling to get multi-select inputs to render with an MUI Autocomplete. I had this working earlier and now I can’t reproduce what was working. It seems that the problem is my custom renderer is not getting the right props. Can anyone see my issue or suggest another way to do this?
Also, I am rendering a button. The button is used to start an OAuth flow. I am having similar issues.
schema:
{
"type": "object",
"properties": {
"enum": {
"type": "string",
"enum": ["foo", "bar"]
},
"enumMulti": {
"type": "array",
"uniqueItems": true,
"items": {
"type": "string",
"enum": ["foo", "bar"]
}
},
"oneOf": {
"type": "string",
"description": "One of the following values",
"oneOf": [
{
"const": "1",
"title": "One"
},
{
"const": "2",
"title": "Two"
}
]
},
"oneOfMulti": {
"type": "array",
"uniqueItems": true,
"items": {
"oneOf": [
{
"const": "1",
"title": "One"
},
{
"const": "2",
"title": "Two"
}
]
}
},
"button": {
"type": "null",
"description": "Click to navigate"
}
},
"required": []
}
uischema:
{
"type": "VerticalLayout",
"elements": [
{
"type": "Control",
"scope": "#/properties/enum"
},
{
"type": "Control",
"scope": "#/properties/enumMulti",
"options": {
"format": "multi"
}
},
{
"type": "Control",
"scope": "#/properties/oneOf"
},
{
"type": "Control",
"label": "Select all that apply",
"scope": "#/properties/oneOfMulti",
"options": {
"format": "multi"
}
},
{
"type": "Control",
"label": "Go",
"scope": "#/properties/button"
}
]
}
data:
{
"enum": "foo",
"enumMulti": ["foo"],
"oneOf": "1",
"oneOfMulti": ["1", "2"],
"button": "https://jsonforms.io"
}
renderers:
const buttonRenderer: JsonFormsRendererRegistryEntry = {
tester: rankWith(6, scopeEndsWith('button')),
renderer: (props: ControlProps) => {
const { data, label, description } = props
const url = isValidURL(data) ? new URL(data) : null
// Always attach a uuid to the url as a search param
if (url) url.searchParams.append('state', uuidv4())
return (
<Tooltip
title={description}
enterDelay={500}
hidden={description ? false : true}
>
<Button
variant='contained'
color='success'
size='large'
href={url ? url.toString() : undefined}
target='_blank'
rel='noopener noreferrer'
>
{label}
</Button>
</Tooltip>
)
}
}
type MultiSelectOption = string | { const: string | number; title: string }
/**
* @description A renderer for multi-select fields. Handles enum and oneOf items.
* @requires uischema.options.format = 'multi'
*/
const multiSelectRenderer: JsonFormsRendererRegistryEntry = {
tester: rankWith(
20,
or(scopeEndsWith('enumMulti'), scopeEndsWith('oneOfMulti'))
),
renderer: (props: ControlProps) => {
const { data, label, description, handleChange, path, schema } = props
const options: MultiSelectOption[] = Object.values(schema?.items || {})[0]
const getOptionLabel = (option: MultiSelectOption) => {
if (typeof option === 'string') {
return option
}
return option.title
}
const isOptionEqualToValue = (option: MultiSelectOption, value) => {
if (typeof option === 'string') {
return option === value
}
return option.const === value
}
const onChange = (_, newValue: MultiSelectOption[]) => {
handleChange(
path,
newValue.map((o: MultiSelectOption) =>
typeof o === 'string' ? o : o.const
)
)
}
return (
<Autocomplete
data-testid='multi-select-renderer'
multiple
disableCloseOnSelect
value={data ?? []}
options={options ?? []}
getOptionLabel={getOptionLabel}
isOptionEqualToValue={isOptionEqualToValue}
onChange={onChange}
renderInput={params => (
<TextField
{...params}
label={label}
variant='outlined'
helperText={description}
/>
)}
/>
)
}
}
const renderers: JsonFormsRendererRegistryEntry[] = [
...materialRenderers,
buttonRenderer,
multiSelectRenderer
]
export default renderers
component:
import { useState } from 'react'
import { JsonForms } from '@jsonforms/react'
import renderers from './renderers'
export default function Form({ schema, uischema, defaultData }) {
const [data, setData] = useState(defaultData)
return (
<JsonForms
schema={schema}
uischema={uischema}
renderers={renderers}
data={data}
onChange={({ data }) => setData(data)}
validationMode='ValidateAndShow'
/>
)
}