Terence/fix pw screen (#676)

* remove duplicate

* remove error message

* remove unnecessary props

* fix boolean logic

* remove e2e logic check

* restore message (opps)

* fixup button jumping

* Update src/app/components/passwordInput/index.styled.ts

Co-authored-by: Den <36603049+dhriaznov@users.noreply.github.com>

* remove

---------

Co-authored-by: Den <36603049+dhriaznov@users.noreply.github.com>
This commit is contained in:
Terence Ng
2024-10-16 14:43:52 +02:00
committed by GitHub
parent 22d4658cdf
commit f275b4050e
7 changed files with 50 additions and 112 deletions

View File

@@ -34,7 +34,7 @@ export const ButtonsContainer = styled.div<{
flexDirection: props.$stackButtonAlignment ? 'column-reverse' : 'row',
alignItems: props.$stackButtonAlignment ? 'center' : 'flex-end',
flex: 1,
marginTop: props.$ifError ? props.theme.spacing(30) : props.theme.spacing(40),
marginTop: props.$ifError ? props.theme.space.xxxl : props.theme.space.xxxxl,
marginBottom: props.theme.space.m,
}));
@@ -47,8 +47,9 @@ export const StyledButton = styled.button({
},
});
export const PasswordStrengthContainer = styled.div((props) => ({
export const PasswordStrengthContainer = styled.div<{ $visibility: boolean }>((props) => ({
...props.theme.typography.body_medium_m,
visibility: props.$visibility ? 'visible' : 'hidden',
display: 'flex',
alignItems: 'center',
width: '100%',

View File

@@ -20,8 +20,6 @@ import {
StyledButton,
} from './index.styled';
const REQUIRED_PASSWORD_LENGTH = 5;
enum PasswordStrength {
NoScore,
PoorScore,
@@ -43,7 +41,6 @@ type Props = {
checkPasswordStrength?: boolean;
stackButtonAlignment?: boolean;
loading?: boolean;
createPasswordFlow?: boolean;
autoFocus?: boolean;
};
@@ -59,18 +56,16 @@ function PasswordInput({
checkPasswordStrength,
stackButtonAlignment = false,
loading,
createPasswordFlow,
autoFocus = false,
}: Props): JSX.Element {
const { t } = useTranslation('translation', { keyPrefix: 'CREATE_PASSWORD_SCREEN' });
const theme = useTheme();
const { t } = useTranslation('translation', { keyPrefix: 'CREATE_PASSWORD_SCREEN' });
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const [passwordStrength, setPasswordStrength] = useState<PasswordStrength>(
PasswordStrength.NoScore,
);
const { score } = zxcvbn(enteredPassword);
const enteredPasswordLength = enteredPassword.length;
const [error, setError] = useState<string>(passwordError ?? '');
const transition = useTransition(passwordStrength, {
from: {
opacity: 0,
@@ -79,6 +74,33 @@ function PasswordInput({
opacity: 1,
},
});
const enteredPasswordLength = enteredPassword.length;
let passwordStrengthLabel;
if (!enteredPassword) {
passwordStrengthLabel = {
color: theme.colors.white_600,
width: '0',
message: '',
};
} else if (score <= PasswordStrength.WeakScore) {
passwordStrengthLabel = {
color: theme.colors.feedback.error,
width: '20%',
message: <p style={{ color: theme.colors.feedback.error }}>{t('PASSWORD_STRENGTH_WEAK')}</p>,
};
} else if (score <= PasswordStrength.AverageScore) {
passwordStrengthLabel = {
color: theme.colors.feedback.caution,
width: '50%',
message: <p>{t('PASSWORD_STRENGTH_MEDIUM')}</p>,
};
} else {
passwordStrengthLabel = {
color: theme.colors.feedback.success,
width: '100%',
message: <p>{t('PASSWORD_STRENGTH_STRONG')}</p>,
};
}
useEffect(() => {
const keyDownHandler = (event) => {
@@ -86,7 +108,6 @@ function PasswordInput({
event.key === 'Enter' &&
document.activeElement?.id === 'password-input' &&
!!enteredPassword &&
enteredPasswordLength >= REQUIRED_PASSWORD_LENGTH &&
(checkPasswordStrength ? score >= PasswordStrength.AverageScore : true)
) {
event.preventDefault();
@@ -97,94 +118,16 @@ function PasswordInput({
return () => {
document.removeEventListener('keydown', keyDownHandler);
};
}, [enteredPassword]);
useEffect(() => {
if (passwordError) {
setError(passwordError);
return;
}
if (enteredPassword && !!createPasswordFlow && score <= PasswordStrength.WeakScore) {
setError(t('PASSWORD_STRENGTH_ERROR'));
return;
}
setError('');
}, [passwordError, enteredPassword]);
}, [checkPasswordStrength, enteredPassword, enteredPasswordLength, handleContinue, score]);
useEffect(() => {
if (enteredPassword !== '') {
setPasswordStrength(score);
}
return () => {
setPasswordStrength(PasswordStrength.NoScore);
};
}, [enteredPassword, setPasswordStrength]);
const handleTogglePasswordView = () => {
setIsPasswordVisible(!isPasswordVisible);
};
const handlePasswordChange = (event: React.FormEvent<HTMLInputElement>) => {
setEnteredPassword(event.currentTarget.value);
};
const renderStrengthBar = () => {
if (enteredPassword !== '') {
if (
enteredPasswordLength <= REQUIRED_PASSWORD_LENGTH ||
score <= PasswordStrength.WeakScore
) {
return (
<PasswordStrengthContainer>
<span>{t('PASSWORD_STRENGTH_LABEL')}</span>
<StrengthBar $strengthColor={theme.colors.feedback.error} $strengthWidth="20%">
{transition((style) => (
<animated.div style={style} />
))}
</StrengthBar>
<p style={{ color: theme.colors.feedback.error }}>{t('PASSWORD_STRENGTH_WEAK')}</p>
</PasswordStrengthContainer>
);
}
if (score <= PasswordStrength.AverageScore) {
return (
<PasswordStrengthContainer>
<span>{t('PASSWORD_STRENGTH_LABEL')}</span>
{transition((style) => (
<StrengthBar $strengthColor={theme.colors.feedback.caution} $strengthWidth="50%">
<animated.div style={style} />
</StrengthBar>
))}
<p>{t('PASSWORD_STRENGTH_MEDIUM')}</p>
</PasswordStrengthContainer>
);
}
return (
<PasswordStrengthContainer>
<span>{t('PASSWORD_STRENGTH_LABEL')}</span>
{transition((style) => (
<StrengthBar $strengthColor={theme.colors.feedback.success} $strengthWidth="100%">
<animated.div style={style} />
</StrengthBar>
))}
<p>{t('PASSWORD_STRENGTH_STRONG')}</p>
</PasswordStrengthContainer>
);
}
return (
<PasswordStrengthContainer>
<span>{t('PASSWORD_STRENGTH_LABEL')}</span>
<StrengthBar $strengthColor={theme.colors.white_600} $strengthWidth="0">
{transition((style) => (
<animated.div style={style} />
))}
</StrengthBar>
</PasswordStrengthContainer>
);
};
}, [enteredPassword, score, setPasswordStrength]);
return (
<Container>
@@ -198,10 +141,10 @@ function PasswordInput({
id="password-input"
type={isPasswordVisible ? 'text' : 'password'}
value={enteredPassword}
onChange={handlePasswordChange}
onChange={(e) => setEnteredPassword(e.currentTarget.value)}
autoFocus={autoFocus}
complications={
<StyledButton onClick={handleTogglePasswordView}>
<StyledButton onClick={() => setIsPasswordVisible(!isPasswordVisible)}>
<img
src={isPasswordVisible ? Eye : EyeSlash}
alt="toggle password visibility"
@@ -209,11 +152,22 @@ function PasswordInput({
/>
</StyledButton>
}
feedback={error !== '' ? [{ message: error, variant: 'danger' }] : undefined}
feedback={passwordError ? [{ message: passwordError, variant: 'danger' }] : undefined}
hideClear
/>
{checkPasswordStrength ? renderStrengthBar() : null}
<ButtonsContainer $stackButtonAlignment={stackButtonAlignment} $ifError={error !== ''}>
<PasswordStrengthContainer $visibility={!!checkPasswordStrength}>
<span>{t('PASSWORD_STRENGTH_LABEL')}</span>
<StrengthBar
$strengthColor={passwordStrengthLabel.color}
$strengthWidth={passwordStrengthLabel.width}
>
{transition((style) => (
<animated.div style={style} />
))}
</StrengthBar>
{passwordStrengthLabel.message}
</PasswordStrengthContainer>
<ButtonsContainer $stackButtonAlignment={stackButtonAlignment} $ifError={!!passwordError}>
<ButtonContainer $stackButtonAlignment={stackButtonAlignment}>
<Button title={t('BACK_BUTTON')} onClick={handleBack} variant="secondary" type="button" />
</ButtonContainer>

View File

@@ -101,7 +101,6 @@ function CreatePassword(): JSX.Element {
handleContinue={handleContinuePasswordCreation}
handleBack={handleNewPasswordBack}
checkPasswordStrength
createPasswordFlow
autoFocus
/>
) : (

View File

@@ -129,7 +129,6 @@ function RestoreWallet(): JSX.Element {
handleContinue={onNewPasswordContinue}
handleBack={onNewPasswordBack}
checkPasswordStrength
createPasswordFlow
autoFocus
/>
</PasswordContainer>,

View File

@@ -93,7 +93,6 @@ function ChangePasswordScreen() {
handleBack={handleBackButtonClick}
checkPasswordStrength
stackButtonAlignment
createPasswordFlow
autoFocus
/>
)}

View File

@@ -594,7 +594,6 @@
"PASSWORD_STRENGTH_WEAK": "Weak",
"PASSWORD_STRENGTH_MEDIUM": "Medium",
"PASSWORD_STRENGTH_STRONG": "Strong",
"PASSWORD_STRENGTH_ERROR": "Your password should be at least 9 characters long. Use a mix of uppercase letters, lowercase letters, numbers, and symbols",
"CONFIRM_PASSWORD_MATCH_ERROR": "Please make sure your passwords match",
"INCORRECT_PASSWORD_ERROR": "Incorrect password"
},

View File

@@ -26,8 +26,6 @@ export default class Onboarding {
readonly inputPassword: Locator;
readonly errorMessage: Locator;
readonly errorMessage2: Locator;
readonly errorMessageSeedPhrase: Locator;
@@ -89,7 +87,6 @@ export default class Onboarding {
this.buttonSeedWords = page.locator('button[value]:not([value=""])');
this.header = page.locator('#app h3');
this.inputPassword = page.locator('input[type="password"]');
this.errorMessage = page.locator('p').filter({ hasText: 'Your password should be at' });
this.errorMessage2 = page.locator('p').filter({ hasText: 'Please make sure your' });
this.errorMessageSeedPhrase = page.locator('p').filter({ hasText: 'Invalid seed phrase' });
this.labelSecurityLevelWeak = page.locator('p').filter({ hasText: 'Weak' });
@@ -186,7 +183,6 @@ export default class Onboarding {
await expect(this.inputPassword).toBeVisible();
await expect(this.buttonContinue).toBeVisible();
await expect(this.buttonContinue).toBeDisabled();
await expect(this.errorMessage).toBeHidden();
await expect(this.labelSecurityLevelWeak).toBeHidden();
await expect(this.labelSecurityLevelMedium).toBeHidden();
await expect(this.labelSecurityLevelStrong).toBeHidden();
@@ -263,15 +259,6 @@ export default class Onboarding {
// Fill in the password input field with the specified password.
await this.inputPassword.fill(password);
// Check if an error message is expected to be visible.
if (expectations.errorMessageVisible) {
// If yes, verify that the error message element is visible.
await expect(this.errorMessage).toBeVisible();
} else {
// If not, verify that the error message element is hidden.
await expect(this.errorMessage).toBeHidden();
}
// Define a mapping of security levels to their corresponding label elements.
const visibilityChecks = {
Weak: this.labelSecurityLevelWeak,