Skip to content

Commit

Permalink
Add dynamic import on zxcvbn
Browse files Browse the repository at this point in the history
Change-type: patch
  • Loading branch information
Andrea Rosci authored and Andrea Rosci committed Mar 4, 2024
1 parent 2d5086a commit 6339752
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 66 deletions.
26 changes: 13 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@storybook/react-webpack5": "^7.6.17",
"@storybook/testing-library": "^0.2.0",
"@storybook/theming": "^7.6.17",
"@types/zxcvbn": "^4.4.2",
"@types/zxcvbn": "^4.4.4",
"babel-plugin-named-exports-order": "^0.0.2",
"eslint-plugin-storybook": "^0.6.13",
"husky": "^8.0.3",
Expand Down
45 changes: 45 additions & 0 deletions src/components/Form/PasswordStrength.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Box, Typography } from '@mui/material';
import { useState, useEffect } from 'react';
import zxcvbn from 'zxcvbn';

const STRENGTH_TITLES = ['Very weak', 'Weak', 'Good', 'Strong', 'Very strong'];
const STRENGTH_STYLES = [
{ width: 0 },
{ width: '25%', backgroundColor: 'orange' },
{ width: '50%', backgroundColor: 'yellow' },
{ width: '75%', backgroundColor: 'green' },
{ width: '100%', backgroundColor: 'darkgreen' },
];

export interface PasswordStrengthProps {
password?: string;
}

export const PasswordStrength = ({ password }: PasswordStrengthProps) => {
const [strengthScore, setStrengthScore] = useState<number | undefined>();

useEffect(() => {
if (password?.length) {
const { score } = zxcvbn(password);
setStrengthScore(score);
}
}, [password]);

if (!password || strengthScore === undefined) {
return null;
}

return (
<Box>
<Typography>
Password strength: <em>{STRENGTH_TITLES[strengthScore]}</em>
</Typography>
<Box
sx={[
STRENGTH_STYLES[strengthScore],
{ height: '4px', transition: 'all ease-out 150ms' },
]}
/>
</Box>
);
};
63 changes: 11 additions & 52 deletions src/components/Form/Widgets/PasswordWidget.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,22 @@
import {
Box,
FormControl,
IconButton,
InputAdornment,
TextField,
Typography,
} from '@mui/material';
import * as React from 'react';
import { Suspense, lazy, useState } from 'react';
import { Visibility, VisibilityOff } from '@mui/icons-material';
import { WidgetProps } from '@rjsf/utils';

const STRENGTH_TITLES = ['Very weak', 'Weak', 'Good', 'Strong', 'Very strong'];
const STRENGTH_STYLES = [
{ width: 0 },
{ width: '25%', backgroundColor: 'orange' },
{ width: '50%', backgroundColor: 'yellow' },
{ width: '75%', backgroundColor: 'green' },
{ width: '100%', backgroundColor: 'darkgreen' },
];

export interface PasswordStrengthProps {
password?: string;
}

const PasswordStrength = ({ password }: PasswordStrengthProps) => {
const [strengthScore, setStrengthScore] = React.useState<
number | undefined
>();

React.useEffect(() => {
// @ts-expect-error If you wish to show a stength meter, you need to load and set `zxcvbn` to a window variable by yourself.
const zxcvbn = window.zxcvbn;

if (zxcvbn && password) {
try {
const { score } = zxcvbn(password);
setStrengthScore(score);
} catch {
// Ignore any errors, as we only want to show the strength meter if it is available.
}
}
}, [password]);

if (!password || strengthScore === undefined) {
return null;
}

return (
<Box>
<Typography>
Password strength: <em>{STRENGTH_TITLES[strengthScore]}</em>
</Typography>
<Box
sx={[
STRENGTH_STYLES[strengthScore],
{ height: '4px', transition: 'all ease-out 150ms' },
]}
/>
</Box>
);
};
const PasswordStrength = lazy(async () => {
const importedModule = await import('../PasswordStrength');
return {
default: importedModule.PasswordStrength,
};
});

export const PasswordWidget = ({
id,
Expand All @@ -74,7 +31,7 @@ export const PasswordWidget = ({
options,
required,
}: WidgetProps) => {
const [showPassword, setShowPassword] = React.useState(false);
const [showPassword, setShowPassword] = useState(false);

const change = ({ target: { value } }: any) => {
return onChange(value === '' ? options.emptyValue : value);
Expand Down Expand Up @@ -121,7 +78,9 @@ export const PasswordWidget = ({
}}
/>
{options.showPasswordStrengthMeter && (
<PasswordStrength password={value} />
<Suspense fallback={null}>
<PasswordStrength password={value} />
</Suspense>
)}
</FormControl>
);
Expand Down

0 comments on commit 6339752

Please sign in to comment.