import {Fragment} from 'react'; import {render} from 'react-dom'; import {css} from '@emotion/react'; import styled from '@emotion/styled'; import throttle from 'lodash/throttle'; import zxcvbn from 'zxcvbn'; import {tct} from 'sentry/locale'; import space from 'sentry/styles/space'; import theme from 'sentry/utils/theme'; /** * NOTE: Do not import this component synchronously. The zxcvbn library is * relatively large. This component should be loaded async as a split chunk. */ /** * The maximum score that zxcvbn reports */ const MAX_SCORE = 5; type Props = { /** * The password value. */ value: string; /** * The color to make the progress bar for each strength level. 5 levels. */ colors?: [string, string, string, string, string]; /** * A set of labels to display for each password strength level. 5 levels. */ labels?: [string, string, string, string, string]; }; const PasswordStrength = ({ value, labels = ['Very Weak', 'Very Weak', 'Weak', 'Strong', 'Very Strong'], colors = [theme.red300, theme.red300, theme.yellow300, theme.green300, theme.green300], }: Props) => { if (value === '') { return null; } const result = zxcvbn(value); if (!result) { return null; } const {score} = result; const percent = Math.round(((score + 1) / MAX_SCORE) * 100); const styles = css` background: ${colors[score]}; width: ${percent}%; `; return ( {tct('Strength: [textScore]', { textScore: {labels[score]}, })} ); }; const StrengthProgress = styled('div')` background: ${theme.gray200}; height: 8px; border-radius: 2px; overflow: hidden; `; const StrengthProgressBar = styled('div')` height: 100%; `; const StrengthLabel = styled('div')` font-size: 0.8em; margin-top: ${space(0.25)}; color: ${theme.gray400}; `; const ScoreText = styled('strong')` color: ${p => p.theme.black}; `; export default PasswordStrength; /** * This is a shim that allows the password strength component to be used * outside of our main react application. Mostly useful since all of our * registration pages aren't in the react app. */ export const attachTo = ({input, element}) => element && input && input.addEventListener( 'input', throttle(e => { render(, element); }) );