How to get a material custom renderer with InputAdornment to work?

Hi,

I am trying to create a custom renderer for a password text field having a right handed icon to disclose the password on click. This is usually implemented using input adornments. Problem is that it seems to be impossible to pass another property to the material renderers except defined in the ControlProps type. So the following implementation has no effect:

import { Unwrapped } from '@jsonforms/material-renderers';
import IconButton from '@mui/material/IconButton';
import InputAdornment from '@mui/material/InputAdornment';
import { Visibility, VisibilityOff } from '@mui/icons-material';

const { MaterialTextControl } = Unwrapped;

const DisclosedPasswordRenderer = ( props ) => {
    const [showPassword, setShowPassword] = useState(false);

    const togglePasswordVisibility = () => {
        setShowPassword(!showPassword);
    };

    const endAdornment = (
        <InputAdornment position="end">
            <IconButton
                aria-label="toggle password visibility"
                onClick={togglePasswordVisibility}
                edge="end"
            >
                {showPassword ? <Visibility /> : <VisibilityOff />}
            </IconButton>
        </InputAdornment>
    );

    return (
        <MaterialTextControl {...props} endAdornmemt={endAdornment} />
    );
};

export default withJsonFormsControlProps(DisclosedPasswordRenderer);

They only way to get it to work is to create a custom renderer not using the built-in material renderer but I fail to implement all the nitty-gritty control properties to get the same look and feel. Here is my attempt:

const DisclosedPasswordControl = ({ data, handleChange, path, label, required }) => {
    const [showPassword, setShowPassword] = useState(false);

    const togglePasswordVisibility = () => {
        setShowPassword(!showPassword);
    };

    return (
        <TextField
            type={showPassword ? 'text' : 'password'}
            label={label}
            value={data || ''}
            onChange={(e) => handleChange(path, e.target.value)}
            InputProps={{
                endAdornment: (
                    <InputAdornment position="end">
                        <IconButton
                            aria-label="toggle password visibility"
                            onClick={togglePasswordVisibility}
                            edge="end"
                        >
                            {showPassword ? <Visibility /> : <VisibilityOff />}
                        </IconButton>
                    </InputAdornment>
                ),
            }}
            fullWidth
            required={showAsRequired(required, '')}
        />

    );
};

Any ideas how this can be implemented either using a “wrapped” version using the material renderer or in any other way so it has the same look-and-feel as the material renderer?

All on JSONForms 3.2.1

Thanks
Dirk

Hi @dirktw,

The MaterialTextControl renders a MaterialInputControl with a MuiInputText. The endAdornment prop will be handed over to the MuiInputText, but there it is ignored as we are not handing over all props to the InputComponent. But even if we did, we would overwrite your prop, as we are configuring our own endAdornment.

As you see, we are using more basic elements from Material UI than a regular TextField. If you would customize the TextField a bit, you could support all of JSON Forms’ features. See for example the Autocomplete on how to configure additional FormHelperTexts.

However the easiest way for you is to instead use the same approach as the MaterialTextControl, i.e.

  • copy the MuiInputText component of JSON Forms
  • adapt it to your liking
  • copy the MaterialTextControl and instead of handing over MuiInputText, you hand over your adapted copy

That’s overall not much effort and you have full control over the rendering.

Hi Stefan,

thanks for your advice. Indeed I got this to work by copying and modifying MuiInputText and MaterialTextControl to a local dir, great!

Strangely it works only using the npm dev mode (npm start) but not when building the application due to importing modules from the relative …/util path which is not available in the local copy dir. So how do I need to adjust the following import code from MuiInputText.tsx to use an absolute path instead of the relative one?

import {
  JsonFormsTheme,
  WithInputProps,
  useDebouncedChange,
  useInputComponent,
} from '../util';

Importing this from ‘@jsonforms/material-renderers’ or from ‘@jsonforms/core’ fails. And there is no ‘@jsonforms/util’

Any hints?

Thanks in advance!

Dirk

import {
  JsonFormsTheme,
  WithInputProps,
  useDebouncedChange,
  useInputComponent,
} from '@jsonforms/material-renderers';

should work as all of these are exported.

Yes, it works, sorry for the confusion!