123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- 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 (
- <Fragment>
- <StrengthProgress
- role="progressbar"
- aria-valuenow={score}
- aria-valuemin={0}
- aria-valuemax={100}
- >
- <StrengthProgressBar css={styles} />
- </StrengthProgress>
- <StrengthLabel>
- {tct('Strength: [textScore]', {
- textScore: <ScoreText>{labels[score]}</ScoreText>,
- })}
- </StrengthLabel>
- </Fragment>
- );
- };
- 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(<PasswordStrength value={e.target.value} />, element);
- })
- );
|