# Mui autocomplete multiple Enum

**URL:** <https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275>\
**Category:** General\
**Created:** [August 12, 2024, 12:53pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275 "2024-08-12T12:53:09Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 12, 2024, 12:53pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/1 "2024-08-12T12:53:09Z")

</div>

Hello,  
I’ve (re)created a multi-select custom renderer based on :

> [@MUI Autocomplete Multi-Select](https://jsonforms.discourse.group/t/mui-autocomplete-multi-select/1105/5):
>
> hello @Richkroll, could you post somewhere your component that we can reuse it ? Thanks !

I’ve also an issue with the validation…  
“must be string must be equal to one of the allowed values” is displayed under the control

my code :

```auto
import React, { ReactNode } from 'react';
import merge from 'lodash/merge';
import isEqual from 'lodash/isEqual';
import {
  ControlProps,
  OwnPropsOfEnum,
  WithClassname,
  EnumOption,
  isDescriptionHidden,
  RankedTester,
  rankWith,
  and,
  isControl,
  isEnumControl,
  optionIs,
} from '@jsonforms/core';
import {
  withJsonFormsEnumProps,
  withJsonFormsControlProps,
  TranslateProps,
  withTranslateProps,
} from '@jsonforms/react';
import { Autocomplete, FormHelperText, Hidden, TextField } from '@mui/material';
import { WithOptionLabel } from '@jsonforms/material-renderers/lib/mui-controls/MuiAutocomplete';
import { useFocus } from '@jsonforms/material-renderers';

export const MultiSelectEnumArray = (
  props: ControlProps & OwnPropsOfEnum & WithClassname & WithOptionLabel,
) => {
  const [focused, onFocus, onBlur] = useFocus();
  const {
    description,
    className,
    config,
    id,
    label,
    required,
    errors,
    data,
    visible,
    options,
    handleChange,
    path,
    enabled,
    getOptionLabel,
  } = props;

  const isValid = errors.length === 0;
  const appliedUiSchemaOptions = merge({}, config, props.uischema.options);

  const showDescription = !isDescriptionHidden(
    visible,
    description,
    focused,
    appliedUiSchemaOptions.showUnfocusedDescription,
  );

  const firstFormHelperText = showDescription
    ? description
    : !isValid
      ? errors
      : null;
  const secondFormHelperText = showDescription && !isValid ? errors : null;

  const onChange = (_ev: any, newValues: EnumOption[]) => {
    // convert from an EnumOption to its value
    var values = newValues.map(o => (o.value ? o.value : o));
    handleChange(path, values);
  };

  return (
    <Hidden xsUp={!visible}>
      <Autocomplete
        multiple
        id={id}
        className={className}
        disabled={!enabled}
        autoHighlight
        autoSelect
        autoComplete
        fullWidth
        freeSolo={false}
        options={options ? options : []}
        value={data}
        getOptionLabel={option => (option.label ? option.label : option)}
        onChange={onChange}
        isOptionEqualToValue={(o, v) => {
          return isEqual(o.value, v);
        }}
        renderInput={params => (
          <TextField
            label={label}
            variant={
              appliedUiSchemaOptions.variant
                ? appliedUiSchemaOptions.variant
                : 'outlined'
            }
            //type="text"
            inputRef={params.InputProps.ref}
            autoFocus={appliedUiSchemaOptions.focus}
            //placeholder="Favorites"
            {...params}
            id={id + '-input'}
            required={required && !appliedUiSchemaOptions.hideRequiredAsterisk}
            error={!isValid}
            fullWidth={!appliedUiSchemaOptions.trim}
            InputLabelProps={data ? { shrink: true } : undefined}
            onFocus={onFocus}
            onBlur={onBlur}
            focused={focused}
          />
        )}
      />
      <FormHelperText error={!isValid && !showDescription}>
        {firstFormHelperText}
      </FormHelperText>
      <FormHelperText error={!isValid}>{secondFormHelperText}</FormHelperText>
    </Hidden>
  );
};

export const MultiSelectEnumArrayTester: RankedTester = rankWith(
  10,
  //isEnumControl,
  and(isEnumControl, optionIs('xxmultiple', true)),
);

export default withJsonFormsEnumProps(
  withTranslateProps(React.memo(MultiSelectEnumArray)),
  false,
);

```

extract schema.json  
“v09”: {  
“title”: “member of ?”,  
“type”: “string”,  
“enum”: [“ABC”, “DEF”, “GHI”, “Forum”],  
},

(extract) uischema.json  
{  
“type”: “Control”,  
“scope”: “#/properties/VENDOR/properties/v09”,  
“options”: {  
“xxmultiple”: true  
}  
},  
I imagine it’s probably something bad on schemas… but what ?!  
Thx!!

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [August 12, 2024, 1:34pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/2 "2024-08-12T13:34:51Z")

</div>

Hi @clysss,

> [@clysss](#):
>
> I’ve also an issue with the validation…  
> “must be string must be equal to one of the allowed values” is displayed under the control

How does the data look like at `VENDOR.v09`? If it’s for example a number then I would expect these two errors two occur.

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 12, 2024, 8:42pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/3 "2024-08-12T20:42:14Z")

</div>

it’s supposed to be string…🙂  
is there anything for you saying that it could be a number ?  
when I look into data, I’ve :  
“VENDOR”: {  
“v09”: [  
“ABC”,  
“Forum”  
],

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [August 13, 2024, 7:37am UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/4 "2024-08-13T07:37:18Z")

</div>

Hi @clysss,

> [@clysss](#):
>
> “VENDOR”: {  
> “v09”: [  
> “ABC”,  
> “Forum”  
> ],

Well that is also invalid. In the JSON Schema it is specified that `v09` is a `string` with a value of either `'ABC'`, `'DEF'`, `'GHI'` or `'Forum'`. However in your data is is actually an array with the value `['ABC', 'Forum']`. This value is not a string and it also does not match one of the `enum` values. This is why you got these two error messages.

You either need to make sure that your data fits to your schema, or adjust the schema to fit to your data.

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 13, 2024, 9:30am UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/5 "2024-08-13T09:30:04Z")

</div>

if I use “array” type in schema, I have the “array” display with [+] button and several select box.  
“type”: “array”,  
“items”: {  
“type”:“string”,  
“enum”: [“ABC”, “DEF”, “Forum”]  
},  
The current data and the current display is what I need. How to define correctly the schema ? or should I change something in the component ?  
Thx

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 13, 2024, 9:35am UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/6 "2024-08-13T09:35:38Z")

</div>

and if i use  
“type”: “array”,  
“enum”: [“ABC”, “DEF”, “Forum”],

i’ve the good display and only the message :  
must be equal to one of the allowed values

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [August 13, 2024, 12:11pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/7 "2024-08-13T12:11:31Z")

</div>

We have an example in the documentation on how to model a multi-select enum in JSON Schema, see [here](https://jsonforms.io/docs/multiple-choice#multi-select).

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 13, 2024, 12:34pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/8 "2024-08-13T12:34:50Z")

</div>

I think that my schema is already like the example…  
with : schema.json =  
“type”: “array”,  
“uniqueItems”: true,  
“options”: { “xxmultiple”: true },  
“items”: {  
“type”: “string”,  
“enum”: [“ABC”, “DEF”,“InCyber Forum”]  
},  
i see the checkboxes and no issue with validation  
with : schema.json =  
“type”: “array”,  
“options”: { “xxmultiple”: true },  
“items”: {  
“type”: “string”,  
“enum”: [“ABC”, “DEF”,“Forum”]  
},  
I see an array [+] of select (so probably not my component)

to register the component, should I use isEnumControl ???

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [August 14, 2024, 1:44pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/9 "2024-08-14T13:44:34Z")

</div>

This is the [tester](https://github.com/eclipsesource/jsonforms/blob/26fd0e90fc1f771630cb678399456e18f8cb9acd/packages/material-renderers/src/complex/MaterialEnumArrayRenderer.tsx#L133-L150) we are using in out off-the-shelf renderer. You can use the same or a simpler one, depending on your requirements.

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 14, 2024, 6:57pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/10 "2024-08-14T18:57:38Z")

</div>

Hello Stefan,  
Thanks a lot for your return  
been able with it to make it work with this tester

there was a last error with withJsonFormsMultiEnumProps instead of withJsonFormsEnumProps  
Thanks to your pointer, as it was the line following the tester, I was able to see it !

I’ve now a multiselect working with Autocomplete instead of Checkboxes

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 14, 2024, 6:59pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/11 "2024-08-14T18:59:08Z")

</div>

```auto
import React, { ReactNode } from 'react';
import merge from 'lodash/merge';
import isEqual from 'lodash/isEqual';
import {
  ControlProps,
  OwnPropsOfEnum,
  WithClassname,
  EnumOption,
  isDescriptionHidden,
  RankedTester,
  rankWith,
  and,
  Paths,
  hasType,
  resolveSchema,
  schemaMatches,
  schemaSubPathMatches,
  showAsRequired,
  uiTypeIs,
  isControl,
  isEnumControl,
  optionIs,
  JsonSchema,
} from '@jsonforms/core';
import {
  withJsonFormsMultiEnumProps,
  withJsonFormsControlProps,
  TranslateProps,
  withTranslateProps,
} from '@jsonforms/react';
import { Autocomplete, FormHelperText, Hidden, TextField } from '@mui/material';
import { WithOptionLabel } from '@jsonforms/material-renderers/lib/mui-controls/MuiAutocomplete';
import { useFocus } from '@jsonforms/material-renderers';

export const MultiSelectEnum = (
  props: ControlProps & OwnPropsOfEnum & WithClassname & WithOptionLabel,
) => {
  const [focused, onFocus, onBlur] = useFocus();
  const {
    description,
    className,
    config,
    id,
    label,
    required,
    errors,
    data,
    visible,
    options,
    handleChange,
    path,
    enabled,
    getOptionLabel,
  } = props;
  //const errors = props.errors;
  const isValid = errors.length === 0;
  const appliedUiSchemaOptions = merge({}, config, props.uischema.options);

  const showDescription = !isDescriptionHidden(
    visible,
    description,
    focused,
    appliedUiSchemaOptions.showUnfocusedDescription,
  );

  const firstFormHelperText = showDescription
    ? description
    : !isValid
      ? errors
      : null;
  const secondFormHelperText = showDescription && !isValid ? errors : null;

  const onChange = (_ev: any, newValues: EnumOption[]) => {
    // convert from an EnumOption to its value
    var values = newValues.map(o => (o.value ? o.value : o));
    handleChange(path, values);
  };

  return (
    <Hidden xsUp={!visible}>
      <Autocomplete
        multiple
        id={id}
        className={className}
        disabled={!enabled}
        autoHighlight
        autoSelect
        autoComplete
        fullWidth
        freeSolo={false}
        options={options ? options : []}
        value={data}
        //getOptionLabel={option => (option.label ? option.label : option)}
        onChange={onChange}
        isOptionEqualToValue={(o, v) => {
          return isEqual(o.value, v);
        }}
        renderInput={params => (
          <TextField
            label={label}
            variant={
              appliedUiSchemaOptions.variant
                ? appliedUiSchemaOptions.variant
                : 'outlined'
            }
            //type="text"
            inputRef={params.InputProps.ref}
            autoFocus={appliedUiSchemaOptions.focus}
            //placeholder="Favorites"
            {...params}
            id={id + '-input'}
            required={required && !appliedUiSchemaOptions.hideRequiredAsterisk}
            error={!isValid}
            fullWidth={!appliedUiSchemaOptions.trim}
            InputLabelProps={data ? { shrink: true } : undefined}
            onFocus={onFocus}
            onBlur={onBlur}
            focused={focused}
          />
        )}
      />
      <FormHelperText error={!isValid && !showDescription}>
        {firstFormHelperText}
      </FormHelperText>
      <FormHelperText error={!isValid}>
        array--{secondFormHelperText}
      </FormHelperText>
    </Hidden>
  );
};

const hasOneOfItems = (schema: JsonSchema): boolean =>
  schema.oneOf !== undefined &&
  schema.oneOf.length > 0 &&
  (schema.oneOf as JsonSchema[]).every((entry: JsonSchema) => {
    return entry.const !== undefined;
  });

const hasEnumItems = (schema: JsonSchema): boolean =>
  schema.type === 'string' && schema.enum !== undefined;

export const MultiSelectEnumTester: RankedTester = rankWith(
  10,
  and(
    uiTypeIs('Control'),
    and(
      schemaMatches(
        schema =>
          hasType(schema, 'array') &&
          !Array.isArray(schema.items) &&
          schema.uniqueItems === true,
      ),
      schemaSubPathMatches('items', (schema, rootSchema) => {
        const resolvedSchema = schema.$ref
          ? resolveSchema(rootSchema, schema.$ref, rootSchema)
          : schema;
        return hasOneOfItems(resolvedSchema) || hasEnumItems(resolvedSchema);
      }),
    ),
  ),
);

export default withJsonFormsMultiEnumProps(
  withTranslateProps(React.memo(MultiSelectEnum)),
  false,
);

```

(still need some light cleaning)  
could we integrate it to jsonforms library ?

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [August 16, 2024, 10:13am UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/12 "2024-08-16T10:13:49Z")

</div>

Hi @clysss,

We generally avoid adding too many alternative renderers for the same concept to the base renderer sets as this increases maintenance effort considerably over time. So there is no need to contribute it to the JSON Forms project itself.

However if you make it available open source, then we’ll happily link to it in the [community section of the website](https://jsonforms.io/community).

However a good contribution would be to move and export the tester to `@jsonforms/core` instead of burying it in `@jsonforms/material-renderers`. The same for the other util functions. This would make the existing and your custom renderers simpler which is always a win.

---

<div class="post-metadata">

**Author:** ![clysss](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/clysss/32/996_2.png) [@clysss](https://jsonforms.discourse.group/u/clysss)\
**Post date:** [August 16, 2024, 6:21pm UTC](https://jsonforms.discourse.group/t/mui-autocomplete-multiple-enum/2275/13 "2024-08-16T18:21:47Z")

</div>

I’ll be happy to follow your guides , but I’m not sure to understand

> However a good contribution would be to move and export the tester to `@jsonforms/core` instead of burying it in `@jsonforms/material-renderers`. The same for the other util functions. This would make the existing and your custom renderers simpler which is always a win.  
> Could you send me an example of what you mean ?

Or if it’s a simple operation, I can also publish first the code on github and let you commit the changes!
