1

I'm trying to create a password validator with certain regex condition in material UI that changes from bullet point to checkbox when conditions are met. enter image description here

So far I've tried using the npm library: NiceInputPassword however this doesn't let me change the state as easily so I'm going back to my regular Material UI. I've read documentation on lists in material UI but I couldn't find anything that allows me to change bullet points to checkboxes as shown in the image.

  • If there is no option to change it by library, you can do it "rough" by changing css classes. It is an option, but I wouldn't recommended – Jakub Štellner Oct 05 '22 at 06:51
  • Can you share the working code you have? It is not clear if you are talking about a CSS/class change or component change for the list and checkbox. If you are using the MUI library then sample code of the problem will enable others to give more specific answers, i.e. which components to use and their API usage – Ash Oct 05 '22 at 07:03
  • @Ash Here is what I was trying but it doesn't work with react hooks: https://codesandbox.io/s/o1v16rqqrz?file=/index.js:280-297 – Angela Rubchinksy Oct 05 '22 at 07:05
  • I'm currently using the textfield component but I'm not sure what else I would need – Angela Rubchinksy Oct 05 '22 at 07:06

2 Answers2

1

I think you can meet all your requirements using @mui/material and @mui/icons-material libraries like this:

import { TextField } from "@mui/material";
import { useEffect, useState } from "react";
import "./styles.css";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import CircleIcon from "@mui/icons-material/Circle";

function checkUppercase(str) {
  for (let i = 0; i < str.length; i++) {
    if (
      str.charAt(i) === str.charAt(i).toUpperCase() &&
      str.charAt(i).match(/[a-z]/i)
    ) {
      return true;
    }
  }
  return false;
}

const SPECIAL_CHARS = "!@#$%^&*()";

function checkSpecialChars(str) {
  const alreadyFoundChars = [];
  for (let i = 0; i < str.length; i++) {
    const currentChar = str[i];
    if (!alreadyFoundChars.includes(currentChar)) {
      if (SPECIAL_CHARS.includes(currentChar)) {
        alreadyFoundChars.push(currentChar);
      }
    }
  }
  return alreadyFoundChars.length >= 3;
}

const getIcon = (isChecked) => {
  const smallDotIcon = <CircleIcon sx={{ fontSize: "0.4rem" }} />;
  const checkCircleIcon = <CheckCircleIcon sx={{ fontSize: "0.8rem" }} />;
  const iconToBeRendered = isChecked ? checkCircleIcon : smallDotIcon;

  return (
    <div
      style={{
        height: "20px",
        width: "15px",
        display: "flex",
        alignItems: "center",
        justifyContent: "center"
      }}
    >
      {iconToBeRendered}
    </div>
  );
};

const prepareHelperText = (value) => {
  const firstIcon = getIcon(value.length >= 8);
  const secondIcon = getIcon(checkUppercase(value));
  const thirdIcon = getIcon(checkSpecialChars(value));

  return (
    <div>
      <div style={{ display: "flex" }}>
        {firstIcon}
        Must contain at least 8 characters
      </div>
      <div style={{ display: "flex" }}>
        {secondIcon} Must contain at least 1 uppercase letter
      </div>
      <div style={{ display: "flex" }}>
        {thirdIcon} Must contain 3 of the 4 types of characters !@#$%^&*()
      </div>
    </div>
  );
};

export default function App() {
  const [value, setValue] = useState("");
  const [helperText, setHelperText] = useState("");

  useEffect(() => {
    setHelperText(prepareHelperText(value));
  }, [value]);

  return (
    <div className="App">
      <TextField
        type="password"
        label="Password"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        helperText={helperText}
        sx={{
          "& .Mui-focused": {
            color: "purple"
          },
          "& label.Mui-focused": {
            color: "purple !important"
          },
          "& .MuiOutlinedInput-root": {
            "&.Mui-focused fieldset": {
              borderColor: "purple"
            }
          }
        }}
      />
    </div>
  );
}

You can take a look at this sandbox for a live working example of this solution.

Ahmet Emre Kilinc
  • 5,489
  • 12
  • 30
  • 42
0

There are a few bits to this to achieve your outcome using the libraries and components, so I'll try to explain as best I can.

React-Nice-Input-Password accepts a prop to provide a custom class name for each of the states and specifically for success you can use <NiceInputPassword successClassName="custom-success". With that added you are now able to style the bullet points and by using the same CSS selectors with your own custom-success name you can set the final style, .input-password__description li.custom-success:before. Note you may be able to provide a less specific selector but I didn't investigate this.

Now for the styles comes the interesting part. Set background-color: transparent so you don't see the current style applied, then add a background image with a Data URL set to the value of the image you want. In this case I used the publicly available SVG code for CheckCircle from MUI Icons and set the width and height of the SVG to 10 each, then set the fill of the <path> to a blue color, fill="%232196F3" where %23 is # encoded for the HEX value.

.input-password__description li.custom-success:before {
  background-color: transparent;
  background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24"><path fill="%232196F3" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path></svg>');
}

If you don't want to be stuck with just the blue color then there are two options to modify:

  • edit the fill of the <path> tag to the design color value, remembering to prefix %23 instead of # for HEX values
  • or, use the filter CSS property to adjust the color, e.g. first bullet point only:
.input-password__description li.custom-success:first-of-type:before {
  filter: hue-rotate(45deg);
}

Lastly, be sure to import the CSS somewhere in your app as appropriate, import './custom.css';

Here is a fork of your work in it's own sandbox showing what I have described. The first success bullet point will be a purple hue and all others will be blue.

Edit ReactNiceInputPassword (forked)

Supporting links:

Ash
  • 11,292
  • 4
  • 27
  • 34