diff --git a/README.md b/README.md index 1f95d6a..63adf59 100644 --- a/README.md +++ b/README.md @@ -122,11 +122,10 @@ sh ./scripts/run-tests.sh

🎇 Finished

- [x] Generate pronounceable passwords +- [x] Show password-strength

🎆 Pendent

-- [ ] Show password-strength - [Back To The Top](#title) --- diff --git a/package.json b/package.json index a4ee6ca..2b2dd34 100644 --- a/package.json +++ b/package.json @@ -5,30 +5,30 @@ "dependencies": { "@password-generator/check-strength": "^2.0.4", "@password-generator/package": "latest", - "polished": "^3.6.7", - "react": "^16.13.1", - "react-dom": "^16.13.1", - "react-scripts": "3.4.1", - "react-toastify": "^6.0.8", - "styled-components": "^5.1.1", - "typescript": "^4.0.3" + "polished": "^4.1.3", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-scripts": "4.0.3", + "react-toastify": "^7.0.4", + "styled-components": "^5.3.0", + "typescript": "^4.3.5" }, "devDependencies": { - "@commitlint/cli": "9.1.1", - "@commitlint/config-conventional": "9.1.1", - "@types/mocha": "^8.0.3", - "@types/node": "^14.0.27", - "@types/react": "^16.9.44", - "@types/react-dom": "^16.9.8", - "@types/styled-components": "^5.1.2", - "@typescript-eslint/eslint-plugin": "^3.9.0", - "@typescript-eslint/parser": "^3.9.0", - "commitizen": "^4.1.2", - "cypress": "5.0.0", + "@commitlint/cli": "13.1.0", + "@commitlint/config-conventional": "13.1.0", + "@types/mocha": "^9.0.0", + "@types/node": "^16.4.2", + "@types/react": "^17.0.15", + "@types/react-dom": "^17.0.9", + "@types/styled-components": "^5.1.11", + "@typescript-eslint/eslint-plugin": "4.0.1", + "@typescript-eslint/parser": "4.0.1", + "commitizen": "^4.2.4", + "cypress": "8.0.0", "cz-conventional-changelog": "^3.2.0", - "eslint": "^6.6.0", + "eslint": "^7.31.0", "eslint-config-airbnb": "^18.2.0", - "eslint-config-prettier": "^6.11.0", + "eslint-config-prettier": "^8.3.0", "eslint-import-resolver-typescript": "^2.2.0", "eslint-plugin-import": "^2.7.0", "eslint-plugin-import-helpers": "^1.1.0", @@ -38,8 +38,8 @@ "eslint-plugin-react-hooks": "^4.0.8", "git-cz": "^4.7.1", "husky": "^4.2.5", - "lint-staged": "^10.2.11", - "prettier": "^2.1.2" + "lint-staged": "^11.1.1", + "prettier": "^2.3.2" }, "scripts": { "commit": "git-cz", diff --git a/src/components/PasswordGeneratorMain/index.tsx b/src/components/PasswordGeneratorMain/index.tsx index 6cdf3da..8a81a90 100644 --- a/src/components/PasswordGeneratorMain/index.tsx +++ b/src/components/PasswordGeneratorMain/index.tsx @@ -1,11 +1,8 @@ -import React, { useState, FormEvent } from 'react'; +import React, { useState } from 'react'; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -import { - checkStrength, - CheckStrengthResult, -} from '@password-generator/check-strength'; +import { checkStrength } from '@password-generator/check-strength'; import { generatePassword } from '@password-generator/package'; import ClipboardIcon from '../../clipboard-icon.png'; @@ -20,15 +17,53 @@ import { GeneratePasswordButton, DefaultInitialTextInput, CheckBox, - PasswordStrengthSpan, + PasswordStrength, } from './styles'; +type IPasswordStrengthRange = + | 'Very Secure' + | 'Secure' + | 'Very Strong' + | 'Strong' + | 'Average' + | 'Weak' + | 'Very Weak' + | ''; + +const passwordStrengthColor = ( + passwordStrengthRange: IPasswordStrengthRange, +): string => { + const color = { + red: '#FA3333', + orange: '#FA6B33', + green: '#8ABC44', + }; + switch (passwordStrengthRange) { + case '': + return color.red; + case 'Very Weak': + return color.red; + case 'Weak': + return color.red; + case 'Average': + return color.orange; + case 'Strong': + return color.orange; + case 'Very Strong': + return color.orange; + case 'Secure': + return color.green; + case 'Very Secure': + return color.green; + default: + return color.red; + } +}; + const PasswordGeneratorMain: React.FC = () => { const [password, setPassword] = useState(''); - const [ - passwordStrength, - setPasswordStrength, - ] = useState(null); + const [passwordStrength, setPasswordStrength] = useState(''); + const [passwordColor, setPasswordColor] = useState(''); const [preferences, setPreferences] = useState({ initialText: '', @@ -46,9 +81,9 @@ const PasswordGeneratorMain: React.FC = () => { const handleCopyToClipboard = () => { if (password) { - navigator.clipboard - .writeText(password) - .then(() => toast.success('Password was copied to your clipboard!')); + navigator.clipboard.writeText(password).then(() => { + toast.success('Password was copied to your clipboard!'); + }); } }; @@ -74,7 +109,7 @@ const PasswordGeneratorMain: React.FC = () => { } }; - const handleFormSubmit = (event: FormEvent) => { + const handleFormSubmit = (event: React.FormEvent) => { event.preventDefault(); try { const passwordGenerated = generatePassword({ @@ -88,9 +123,12 @@ const PasswordGeneratorMain: React.FC = () => { pronounceable: preferences.pronounceable, }, }); - if (passwordGenerated) { + if (passwordGenerated !== null) { setPassword(passwordGenerated); - setPasswordStrength(checkStrength(passwordGenerated)); + const { points, range } = checkStrength(passwordGenerated); + const color = passwordStrengthColor(range as IPasswordStrengthRange); + setPasswordStrength(`${points}% (${range})`); + setPasswordColor(color); } } catch (error) { toast.error(error.message); @@ -112,9 +150,9 @@ const PasswordGeneratorMain: React.FC = () => { - - {passwordStrength?.range} - + + {passwordStrength} +
diff --git a/src/components/PasswordGeneratorMain/styles.ts b/src/components/PasswordGeneratorMain/styles.ts index e21d2c1..c16938b 100644 --- a/src/components/PasswordGeneratorMain/styles.ts +++ b/src/components/PasswordGeneratorMain/styles.ts @@ -4,7 +4,7 @@ import { lighten } from 'polished'; export const Container = styled.form` background-color: ${(props) => props.theme.mainBlue}; - box-shadow: 0px 2px 10px ${(props) => props.theme.gray}; + box-shadow: 0 2px 10px ${(props) => props.theme.gray}; padding: 18px; border-radius: 20px; width: 80%; @@ -28,6 +28,7 @@ export const ResultContainer = styled.div` height: 30px; width: 95%; `; + export const ResultSpan = styled.input` background-color: ${(props) => lighten(0.1, props.theme.blackBlue)}; color: #fff; @@ -38,27 +39,16 @@ export const ResultSpan = styled.input` max-height: 23.2px; min-width: calc(100% - 35px); max-width: calc(100% - 40px); + &::selection { background-color: ${(props) => props.theme.gray}; } `; -export const PasswordStrengthSpan = styled.span.attrs((props: any) => ({ - type: 'number', - passwordPoints: props.passwordPoints || 0, -}))` - color: ${(props) => props.theme.blackBlue}; +export const PasswordStrength = styled.div` + color: ${(props) => (props.color ? '#fffff' : props.theme.mainBlue)}; font-weight: bolder; - background-color: ${(props) => { - let background: string; - - if (props.passwordPoints === 0) background = props.theme.mainBlue; - else if (props.passwordPoints <= 25) background = 'red'; - else if (props.passwordPoints <= 60) background = 'yellow'; - else background = 'green'; - - return background; - }}; + background-color: ${(props) => props.color}; display: flex; justify-content: center; align-items: center; @@ -89,6 +79,7 @@ export const PasswordLengthInput = styled.input.attrs({ max: '1024', })` font-size: 18px; + &::-webkit-inner-spin-button { width: 15px; height: 35px; diff --git a/tsconfig.json b/tsconfig.json index db1807b..29a58af 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,29 +1,29 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "types": [ - "cypress" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "noEmit": true, - "jsx": "react", - "isolatedModules": true - }, - "include": [ - "src", - "cypress" - ] -} +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "types": [ + "cypress" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "noEmit": true, + "jsx": "react", + "isolatedModules": true + }, + "include": [ + "src", + "cypress" + ] +}