Skip to content

Commit 7eebda6

Browse files
authored
Add password requirements to login page (#103)
closes #90 Would appreciate some feedback on placement and styling - wanted to fit it into the `Label` element but thought it might be better after the form field
1 parent 36999ce commit 7eebda6

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

packages/app/src/AuthPage.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useEffect } from 'react';
77
import Link from 'next/link';
88
import cx from 'classnames';
99

10+
import { PasswordCheck, CheckOrX } from './PasswordCheck';
1011
import LandingHeader from './LandingHeader';
1112
import * as config from './config';
1213
import api from './api';
@@ -24,6 +25,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
2425
handleSubmit,
2526
formState: { errors, isSubmitting },
2627
setError,
28+
watch,
2729
} = useForm<FormData>({
2830
reValidateMode: 'onSubmit',
2931
});
@@ -45,6 +47,13 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
4547
}
4648
}, [installation, isRegister, router]);
4749

50+
const currentPassword = watch('password', '');
51+
const confirmPassword = watch('confirmPassword', '');
52+
53+
const confirmPass = () => {
54+
return currentPassword === confirmPassword;
55+
};
56+
4857
const onSubmit: SubmitHandler<FormData> = data =>
4958
registerPassword.mutate(
5059
{
@@ -153,15 +162,21 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
153162
htmlFor="confirmPassword"
154163
className="text-start text-muted fs-7.5 mb-1"
155164
>
156-
Confirm Password
165+
<CheckOrX
166+
handler={confirmPass}
167+
password={currentPassword}
168+
>
169+
Confirm Password
170+
</CheckOrX>
157171
</Form.Label>
158172
<Form.Control
159173
data-test-id="form-confirm-password"
160174
id="confirmPassword"
161175
type="password"
162-
className="border-0"
176+
className="border-0 mb-2"
163177
{...form.confirmPassword}
164178
/>
179+
<PasswordCheck password={currentPassword} />
165180
</>
166181
)}
167182
{isRegister && Object.keys(errors).length > 0 && (

packages/app/src/PasswordCheck.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useMemo } from 'react';
2+
3+
const checkLength = (password: string) => password.length >= 12;
4+
const checkOneUpper = (password: string) => /[A-Z]+/.test(password);
5+
const checkOneLower = (password: string) => /[a-z]+/.test(password);
6+
const checkOneNumber = (password: string) => /\d+/.test(password);
7+
const checkOneSpecial = (password: string) => /\W+/.test(password);
8+
9+
export const PasswordCheck = (opts: { password: string }) => {
10+
const password = opts.password;
11+
return (
12+
<div>
13+
<div>
14+
<CheckOrX handler={checkLength} password={password}>
15+
minimum 12 characters
16+
</CheckOrX>
17+
</div>
18+
<div>
19+
<CheckOrX handler={checkOneUpper} password={password}>
20+
at least 1 uppercase
21+
</CheckOrX>
22+
</div>
23+
<div>
24+
<CheckOrX handler={checkOneLower} password={password}>
25+
at least 1 lowercase
26+
</CheckOrX>
27+
</div>
28+
<div>
29+
<CheckOrX handler={checkOneNumber} password={password}>
30+
at least 1 number
31+
</CheckOrX>
32+
</div>
33+
<div>
34+
<CheckOrX handler={checkOneSpecial} password={password}>
35+
at least 1 special character
36+
</CheckOrX>
37+
</div>
38+
</div>
39+
);
40+
};
41+
42+
export const CheckOrX = ({
43+
handler,
44+
password,
45+
children,
46+
}: {
47+
handler: (password: string) => boolean;
48+
password: string | { password: string | null };
49+
children: React.ReactNode;
50+
}) => {
51+
let actualPassword = '';
52+
if (typeof password === 'string') {
53+
actualPassword = password;
54+
} else {
55+
actualPassword = password.password ?? '';
56+
}
57+
const isValid = useMemo(
58+
() => handler(actualPassword),
59+
[handler, actualPassword],
60+
);
61+
return (
62+
<span className={isValid ? 'text-success' : 'text-danger'}>
63+
{isValid ? <Check /> : <XShape />} {children}
64+
</span>
65+
);
66+
};
67+
68+
const Check = () => <i className={'bi bi-check2'}></i>;
69+
70+
const XShape = () => <i className={'bi bi-x'}></i>;

0 commit comments

Comments
 (0)