├── .gitignore ├── src ├── components │ ├── Xmark │ │ ├── index.js │ │ └── Xmark.js │ ├── Checkmark │ │ ├── index.js │ │ └── Checkmark.js │ ├── Requirement │ │ ├── index.js │ │ └── Requirement.js │ └── Requirements │ │ ├── index.js │ │ └── Requirements.js ├── index.js ├── stories │ ├── styles.css │ └── Requirements.stories.js └── styles.css ├── .storybook ├── preview.js └── main.js ├── rollup.config.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /src/components/Xmark/index.js: -------------------------------------------------------------------------------- 1 | export * from './Xmark'; 2 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export * from './components/Requirements'; 2 | -------------------------------------------------------------------------------- /src/components/Checkmark/index.js: -------------------------------------------------------------------------------- 1 | export * from './Checkmark'; 2 | -------------------------------------------------------------------------------- /src/components/Requirement/index.js: -------------------------------------------------------------------------------- 1 | export * from './Requirement'; 2 | -------------------------------------------------------------------------------- /src/components/Requirements/index.js: -------------------------------------------------------------------------------- 1 | export * from './Requirements'; 2 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | 2 | export const parameters = { 3 | actions: { argTypesRegex: "^on[A-Z].*" }, 4 | } -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": [ 3 | "../src/**/*.stories.mdx", 4 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 5 | ], 6 | "addons": [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials" 9 | ] 10 | } -------------------------------------------------------------------------------- /src/components/Checkmark/Checkmark.js: -------------------------------------------------------------------------------- 1 | // Ported from Alexander Haniotis' code here: https://codepen.io/haniotis/pen/KwvYLO 2 | import React from "react"; 3 | 4 | export const Checkmark = () => { 5 | return ( 6 | 9 | 16 | 21 | 22 | ); 23 | } -------------------------------------------------------------------------------- /src/components/Requirement/Requirement.js: -------------------------------------------------------------------------------- 1 | import { Xmark } from "../Xmark"; 2 | import { Checkmark } from "../Checkmark"; 3 | import React, { useEffect, useState } from "react"; 4 | 5 | export const Requirement = ({ value, requirement }) => { 6 | const [isValid, setIsValid] = useState(); 7 | 8 | useEffect(() => { 9 | setIsValid(requirement.validator(value)); 10 | }, [value, requirement]); 11 | 12 | return ( 13 |
14 | {isValid ? : } 15 | 16 |

17 | {requirement.text} 18 |

19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/Requirements/Requirements.js: -------------------------------------------------------------------------------- 1 | import '../../styles.css'; 2 | import { Requirement } from '../Requirement'; 3 | import React, { useCallback, useEffect } from 'react'; 4 | 5 | export const Requirements = ({ 6 | value, 7 | requirements, 8 | onValidChange, 9 | }) => { 10 | const validChangeCb = useCallback(onValidChange, []); 11 | 12 | useEffect(() => { 13 | validChangeCb( 14 | requirements.every(r => r.validator(value)) 15 | ); 16 | }, [value, requirements, validChangeCb]); 17 | 18 | return requirements.map((r, index) => ( 19 | 25 | )); 26 | }; 27 | -------------------------------------------------------------------------------- /src/stories/styles.css: -------------------------------------------------------------------------------- 1 | .form { 2 | width: 500px; 3 | padding: 25px; 4 | border-radius: 5px; 5 | padding-bottom: 50px; 6 | box-shadow: 0px 0px 5px gray; 7 | } 8 | .form input { 9 | width: 100%; 10 | border: none; 11 | padding: 8px; 12 | outline: none; 13 | margin-top: 25px; 14 | border-radius: 5px; 15 | box-sizing: border-box; 16 | box-shadow: 0px 0px 3px gray; 17 | } 18 | .form button { 19 | width: 100%; 20 | padding: 8px; 21 | border: none; 22 | outline: none; 23 | color: white; 24 | margin-top: 25px; 25 | border-radius: 5px; 26 | box-sizing: border-box; 27 | background-color: rgb(65, 65, 201); 28 | } 29 | .form button:disabled { 30 | cursor: not-allowed; 31 | background-color: lightgray; 32 | } 33 | .form h1 { 34 | font-family: sans-serif; 35 | } 36 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import external from 'rollup-plugin-peer-deps-external'; 4 | import { terser } from 'rollup-plugin-terser'; 5 | import postcss from 'rollup-plugin-postcss'; 6 | 7 | export default [ 8 | { 9 | input: './src/index.js', 10 | output: [ 11 | { 12 | file: 'dist/index.js', 13 | format: 'cjs', 14 | }, 15 | { 16 | file: 'dist/index.es.js', 17 | format: 'es', 18 | exports: 'named', 19 | } 20 | ], 21 | plugins: [ 22 | postcss({ 23 | plugins: [], 24 | minimize: true, 25 | }), 26 | babel({ 27 | exclude: 'node_modules/**', 28 | presets: ['@babel/preset-react'] 29 | }), 30 | external(), 31 | resolve(), 32 | terser(), 33 | ] 34 | } 35 | ]; 36 | -------------------------------------------------------------------------------- /src/components/Xmark/Xmark.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Xmark = () => { 4 | return ( 5 | 12 | 13 | 18 | 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .requirement { 2 | height: 35px; 3 | display: flex; 4 | align-items: center; 5 | } 6 | .requirement p { 7 | font-size: 14px; 8 | margin-left: 10px; 9 | font-weight: bold; 10 | font-family: sans-serif; 11 | } 12 | .invalid { 13 | color: red; 14 | } 15 | .valid { 16 | color: #7ac142; 17 | } 18 | 19 | /* Ported from Alexander Haniotis' code here: https://codepen.io/haniotis/pen/KwvYLO */ 20 | .checkmark { 21 | width: 15px; 22 | height: 15px; 23 | border-radius: 50%; 24 | display: block; 25 | stroke-width: 5; 26 | stroke: #fff; 27 | stroke-miterlimit: 10; 28 | box-shadow: inset 0px 0px 0px #7ac142; 29 | animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both; 30 | } 31 | 32 | .checkmark__circle { 33 | stroke-dasharray: 166; 34 | stroke-dashoffset: 166; 35 | stroke-width: 2; 36 | stroke-miterlimit: 10; 37 | stroke: #7ac142; 38 | fill: none; 39 | animation: stroke .6s cubic-bezier(0.650, 0.000, 0.450, 1.000) forwards; 40 | } 41 | 42 | .checkmark__check { 43 | transform-origin: 50% 50%; 44 | stroke-dasharray: 48; 45 | stroke-dashoffset: 48; 46 | animation: stroke .2s cubic-bezier(0.650, 0.000, 0.450, 1.000) .5s forwards; 47 | } 48 | 49 | @keyframes stroke { 50 | 100% { 51 | stroke-dashoffset: 0; 52 | } 53 | } 54 | 55 | @keyframes scale { 56 | 0%, 100% { 57 | transform: none; 58 | } 59 | 50% { 60 | transform: scale3d(1.1, 1.1, 1); 61 | } 62 | } 63 | 64 | @keyframes fill { 65 | 100% { 66 | box-shadow: inset 0px 0px 0px 30px #7ac142; 67 | } 68 | } -------------------------------------------------------------------------------- /src/stories/Requirements.stories.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { storiesOf } from '@storybook/react'; 3 | import './styles.css'; 4 | import { Requirements } from '../components/Requirements'; 5 | 6 | const stories = storiesOf('App Test', module); 7 | 8 | stories.add('App', () => { 9 | const [valid, setValid] = useState(false); 10 | const [password, setPassword] = useState(''); 11 | const [username, setUsername] = useState(''); 12 | 13 | const passwordRequirements = [ 14 | { 15 | text: 'Must be at least 8 characters', 16 | validator: val => val.length >= 8, 17 | }, 18 | { 19 | text: 'Must contain at least one number', 20 | validator: val => /\d/g.test(val), 21 | }, 22 | { 23 | text: 'Must contain at least one lower-case letter', 24 | validator: val => /[a-z]/g.test(val), 25 | }, 26 | { 27 | text: 'Must contain at least one upper-case letter', 28 | validator: val => /[A-Z]/g.test(val), 29 | } 30 | ]; 31 | 32 | return ( 33 |
34 |

Signup

35 | 36 | setValid(isValid)} 40 | /> 41 | 42 | setUsername(e.target.value)} /> 43 | setPassword(e.target.value)} /> 44 | 45 | 46 |
47 | ); 48 | }); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-password-validator", 3 | "version": "1.0.0", 4 | "description": "A simple react component that accept password and validate it for more easy useage", 5 | "main": "dist/index.js", 6 | "module": "dist/index.es.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "storybook": "start-storybook -p 6006", 10 | "build-storybook": "build-storybook", 11 | "build-lib": "rollup -c" 12 | }, 13 | "author": "Alan Binu", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@babel/core": "^7.12.10", 17 | "@babel/preset-react": "^7.12.10", 18 | "@rollup/plugin-node-resolve": "^11.1.1", 19 | "@storybook/addon-actions": "^6.1.16", 20 | "@storybook/addon-essentials": "^6.1.16", 21 | "@storybook/addon-links": "^6.1.16", 22 | "@storybook/react": "^6.1.16", 23 | "babel-loader": "^8.2.2", 24 | "react": "^17.0.1", 25 | "react-dom": "^17.0.1", 26 | "rollup": "^2.38.4", 27 | "rollup-plugin-babel": "^4.4.0", 28 | "rollup-plugin-peer-deps-external": "^2.2.4", 29 | "rollup-plugin-postcss": "^4.0.0", 30 | "rollup-plugin-terser": "^7.0.2" 31 | }, 32 | "peerDependencies": { 33 | "react": "^17.0.1", 34 | "react-dom": "^17.0.1" 35 | }, 36 | 37 | "repository": { 38 | "type": "git", 39 | "url": "git+https://github.com/benawad/tsconfig.json.git" 40 | }, 41 | "bugs": { 42 | "url": "https://github.com/benawad/tsconfig.json/issues" 43 | }, 44 | "publishConfig": { 45 | "access": "public", 46 | "branches": [ 47 | "master" 48 | ] 49 | }, 50 | "homepage": "https://github.com/benawad/tsconfig.json#readme", 51 | "dependencies": {} 52 | } 53 | --------------------------------------------------------------------------------