rjsf-team / react-jsonschema-form

A React component for building Web forms from JSON Schema.
https://rjsf-team.github.io/react-jsonschema-form/
Apache License 2.0
14.38k stars 2.19k forks source link

Add a PasswordWidget to MUI #4274

Open TheOneTheOnlyJJ opened 3 months ago

TheOneTheOnlyJJ commented 3 months ago

Prerequisites

What theme are you using?

mui

Is your feature request related to a problem? Please describe.

I found out that there is no PasswordWidget in the @rjsf/mui package. Given how similar to a simple TextField such a widget is, can we add it to the standard distribution?

Describe the solution you'd like

No response

Describe alternatives you've considered

No response

nickgros commented 3 months ago

@TheOneTheOnlyJJ Why would this be needed? The core PasswordWidget works with MUI as it passes the correct prop to BaseInputTemplate, which then gets passed to MUI's TextField. See the playground with the MUI v5 theme. The password field already works.

TheOneTheOnlyJJ commented 3 months ago

@TheOneTheOnlyJJ Why would this be needed? The core PasswordWidget works with MUI as it passes the correct prop to BaseInputTemplate, which then gets passed to MUI's TextField. See the playground with the MUI v5 theme. The password field already works.

I opened this issue thinking that a password widget must also allow you to show/hide the password one way or another, for example through a typical eye icon adornment at the edge of the input. However, given that this is not a standard feature of password inputs, your observation makes sense.

In my project, I wanted to be able to show/hide the password, and I've come up with this solution:

import IconButton from "@mui/material/IconButton/IconButton";
import InputAdornment from "@mui/material/InputAdornment/InputAdornment";
import useTheme from "@mui/material/styles/useTheme";
import TextField from "@mui/material/TextField/TextField";
import VisibilityIcon from "@mui/icons-material/Visibility";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import { WidgetProps } from "@rjsf/utils";
import { FocusEvent, ChangeEvent, FC, useState, useMemo } from "react";

const PasswordWidget: FC<WidgetProps> = (props: WidgetProps) => {
  const { id, required, disabled, readonly, options, rawErrors, onChange, onBlur, onFocus } = props;
  const _onChange = ({ target: { value } }: ChangeEvent<HTMLInputElement>): void => {
    onChange(value === "" ? options.emptyValue : value);
  };
  const _onBlur = ({ target }: FocusEvent<HTMLInputElement>): void => {
    onBlur(id, target.value);
  };
  const _onFocus = ({ target }: FocusEvent<HTMLInputElement>): void => {
    onFocus(id, target.value);
  };
  const [doShowPassword, setDoShowPassword] = useState<boolean>(false);

  const hasError: boolean = useMemo((): boolean => {
    return rawErrors !== undefined && rawErrors.length > 0;
  }, [rawErrors]);

  const theme = useTheme();

  const iconColor: string = useMemo((): string => {
    return hasError ? theme.palette.error.main : theme.palette.text.secondary;
  }, [theme, hasError]);

  return (
    <TextField
      id={id}
      type={doShowPassword ? "text" : "password"}
      label={props.schema.title ?? props.id}
      value={(props.value as string) || ""}
      required={required}
      disabled={disabled}
      onChange={_onChange}
      onBlur={_onBlur}
      onFocus={_onFocus}
      variant="outlined"
      fullWidth
      error={hasError}
      InputProps={{
        readOnly: readonly,
        endAdornment: (
          <InputAdornment position="end">
            <IconButton
              aria-label="toggle password visibility"
              onClick={(): void => {
                setDoShowPassword((prevShowPassword) => !prevShowPassword);
              }}
            >
              {doShowPassword ? <VisibilityIcon style={{ color: iconColor }} /> : <VisibilityOffIcon style={{ color: iconColor }} />}
            </IconButton>
          </InputAdornment>
        )
      }}
    />
  );
};

export default PasswordWidget;

This code was generated with ChatGPT-4o and then typed and polished manually by myself. I'm sure it could be polished even further, but it does its job. For anyone from the future looking for this, you can copy all of this into a new file and start using it directly in your UI Schemas. The Material UI version I used it with is 5.16.7. It might need tweaks if used with newer/older versions.

glebcha commented 2 months ago

@TheOneTheOnlyJJ Such custom password field can be implemented as custom widget. I did nearly the same in my project.

TheOneTheOnlyJJ commented 2 months ago

@TheOneTheOnlyJJ Such custom password field can be implemented as custom widget. I did nearly the same in my project.

Would you share your implementation here? This way, everyone stumbling upon this has a greater sample size of code to look at & add to their projects.

glebcha commented 2 months ago

@TheOneTheOnlyJJ Such custom password field can be implemented as custom widget. I did nearly the same in my project.

Would you share your implementation here? This way, everyone stumbling upon this has a greater sample size of code to look at & add to their projects.

There is shared example in discussions https://github.com/rjsf-team/react-jsonschema-form/discussions/4058