Build This Cool Password Checklist with React and CSS
Tags: react, css, tutorial, javascript
Published: 2020-08-30 04:45:11

My other [blog about building cool sign up form](https://dev.to/cooljasonmelton/build-this-cool-sign-in-form-with-react-and-css-10of) got a lot of attention, so I decided to write another tutorial for a ~cool form~.

> *Edit: I'm getting feedback that password checklists are an antipattern. However, you could easily adapt the concepts in this project to make a to-do list or cross off ingredients on a recipe, etc. See this [thread](https://dev.to/tbroyer/comment/14952) for more info.*

This is a tutorial for how to build this animated password checklist:

![demo gif](https://dev-to-uploads.s3.amazonaws.com/i/tm780pjq3ykcg3yrxpd9.gif)
   
>[get the code on GitHub](https://github.com/cooljasonmelton/password-checklist)

# Tutorial

### Table of Contents
* Preliminary Junk
* Update Password Form
* MustContainItem Component
* Validation Checklist Functionality

### Preliminary Junk

![terminal](https://dev-to-uploads.s3.amazonaws.com/i/nibuga34zbea5dyb9bkm.png)

To create this demo, I entered `create-react-app` into my terminal, deleted the preliminary junk, and set up a file structure like this:

![file structure](https://dev-to-uploads.s3.amazonaws.com/i/x0gct0q0qxroc6kaviqb.png)

I added some basic styles to things and created a class called `cfb` that adds a `flexbox` centering the children of an element to which it’s applied. Also, I found out some nice colors using [Coolors.co](https://coolors.co/ef476f-ffd166-06d6a0-daf3fc-073b4c).

### Update Password Form

This project’s purpose is to validate a password as it’s entered into an update form. The first thing I need is a password to validate. In `UpdatePasswordContainer.js`, I set up a form:

```react
const UpdatePasswordContainer = () => {
  // form inputs
  const [passwordOne, setPasswordOne] = useState("")
  const [passwordTwo, setPasswordTwo] = useState("")
...
  return (
    <div className="UpdatePasswordContainer cfb">
        <h1> Update Password</h1>
        {/* update password form */}
        <form className="password-form cfb">
          {/* password one */}
            <label> 
              new password: 
              <br/>
             <input type="password" 
              value={passwordOne} 
              onChange={e=>setPasswordOne(e.target.value)} 
              onKeyUp={validatePassword}/>
            </label>
          {/* password two */}
            <label>
              re-enter password: 
              <br/>
              <input type="password" 
                value={passwordTwo} 
                onChange={e=>setPasswordTwo(e.target.value)} 
                onKeyUp={validatePassword} />
            </label>
            {/* input button */}
            <input 
              className="submit-button"
              type="submit" 
              disabled={!allValid}/>
        </form>
...
```
I will explain `validatePassword()` in detail later, but it checks the password to make sure it meets the requirements. 

The `allValid` variable enables the submit button if the password passes all requirements.

Otherwise, this is a fairly typical React form. It’s inputs update the `useState` hook `onChange` for each input and then displays the `useState` variable as its value.

### MustContainItem Component

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/53hnjt7qqccsdri9onro.png)

Each item on the list will be represented by a `MustContainItem` component which looks like this:

```react
const MustContainItem = props => {
    const { data } = props
    const label = data[0] // name of item
    const meetsReq = data[1] // boolean of whether or not req is satisfied

    // returns string of classes based on props aliased meetsReq
    const setClass = () => {
        const classArr = ["must-line"]
        if (meetsReq) classArr.push('cross-out')
        return classArr.join(' ')
    }

    return (
        <div className="must-item">
            <li className="must-text">{label}</li>
            <div className={setClass()}></div>
        </div>
  );
}

export default MustContainItem;
```

The `MustContainItem` takes `props` of `data`, which is an array containing a string that labels the item and a boolean of whether or not the requirement has been met.

In the div with class `must-item`, I put `label` in a li node. Then I created a div that receives its class from a function called `setClass()`. 

`setClass()` returns a string of classnames that will conditionally animate a line striking through `label` based on `meetsReq`, the boolean from `props`. 

Here’s the CSS:
```css

.must-container{
    margin-bottom: 1em;
    border: 0.1em solid #073B4C;
}

.must-item{
    position: relative;

    width: 15em;
    height: 1em;

    padding: 0.2em 0;

    border-bottom: 0.1em solid #073B4C;
    background-color: #DAF3FC;
}

.must-text{
    position: absolute;
    z-index: 0;
    top: 0.6em;
    left: 1em;

    width: 100%;

    padding: 0;
    margin: 0;
    line-height: 0;
}

.must-line{
    position: absolute;
    z-index: 1;
    top: 0.5em;
    
    width: 0;
    height: 0.2em;
    
    background-color: #EF476F;
        
    transition: 0.5s;
}

.cross-out{
    width: 100%;
}
```

I give the `must-item` parent container a `position: relative;` so that the child li node and `must-line` div are positioned absolutely on top of each other.

`must-line` gets a `z-index` of 1. It's positioned on top of `must-text` which has a `z-index` of 0. It will cover the text when it appears.

Initially, `must-line` gets a `width` of 0. When I add the class `cross-out`, the div’s `width` expands to 100% and, due to `transition`, the div's `width` animates between sizes.

### Validation Checklist Functionality

The final step is to actually check the password to see if it meets the requirements and update all the relevant components.

First, I set up a `useState` hook for each requirement that I want to put on a new password. Then, I set up a hook for when all the requirements are met, `allValid`.

```react
  // booleans for password validations
  const [containsUL, setContainsUL] = useState(false) // uppercase letter
  const [containsLL, setContainsLL] = useState(false) // lowercase letter
  const [containsN, setContainsN] = useState(false) // number
  const [containsSC, setContainsSC] = useState(false) // special character
  const [contains8C, setContains8C] = useState(false) // min 8 characters
  const [passwordMatch, setPasswordMatch] = useState(false) // passwords match

  // checks all validations are true
  const [allValid, setAllValid] = useState(false)
```

Next, I set up an array of arrays. Each of the inner arrays contains the label string and corresponding `useState` variable to pass to a `MustContainItem` as `props`.

```react
  // labels and state boolean corresponding to each validation
  const mustContainData = [
    ["An uppercase letter (a-z)", containsUL],
    ["A lowercase letter (A-Z)", containsLL],
    ["A number (0-9)", containsN],
    ["A special character (!@#$)", containsSC],
    ["At least 8 characters", contains8C],
    ["Passwords match", passwordMatch]
  ]
```

I `map()` this array creating a `MustContainItem` for each array in the array, passed to the component as `props` named `data`.

```react
  {/* creates a MustContainItem for each password validation with props of label and boolean of state */}
  <h4>Must contain:</h4>
  <div className="must-container cfb">
    {mustContainData.map(data=> <MustContainItem data={data}/>)}
```

Everything is set up at this point except for the actual validating of the password.

I check the password after each character is added or deleted in case a required character is added and then later deleted. For this, I used `onKeyUp`. Each time, a key is lifted, I run the function `validatePassword()`.

```react
    <input type="password" 
        value={passwordOne} 
        onChange={e=>setPasswordOne(e.target.value)} 
        onKeyUp={validatePassword}/>
```

`validatePassword()` runs a series of conditional statements.

```react
 const validatePassword = () => {
    // has uppercase letter
    if (passwordOne.toLowerCase() != passwordOne) setContainsUL(true)
    else setContainsUL(false)

    // has lowercase letter
    if (passwordOne.toUpperCase() != passwordOne) setContainsLL(true)
    else setContainsLL(false)

    // has number
    if (/\d/.test(passwordOne)) setContainsN(true)
    else setContainsN(false)

    // has special character
    if (/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g.test(passwordOne)) setContainsSC(true)
    else setContainsSC(false)

    // has 8 characters
    if (passwordOne.length >= 8) setContains8C(true)
    else setContains8C(false)

    // passwords match
    if (passwordOne !== "" && passwordOne === passwordTwo) setPasswordMatch(true)
    else setPasswordMatch(false)

    // all validations passed
    if (containsUL && containsLL && containsN && containsSC && contains8C && passwordMatch) setAllValid(true)
    else setAllValid(false)
  }
```

Each conditional checks the `passwordOne` variable stored in a `useState` hook for a different thing. If `passwordOne` meets the requirement, it updates that specific requirement’s `useState` hook. If the hook updates, the strikethrough animation is triggered. Viola.

Hope this is interesting or helpful!
Again, you can find all the code [here](https://github.com/cooljasonmelton/password-checklist).

Best, Jason.