Skip to content

Added Show/Hide Toogle to Password Field #2981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 64 additions & 16 deletions client/modules/User/components/AccountForm.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useState } from 'react';
import { Form, Field } from 'react-final-form';
import { useSelector, useDispatch } from 'react-redux';
import { useTranslation } from 'react-i18next';
Expand All @@ -25,6 +26,17 @@ function asyncValidate(fieldToValidate, value) {
}

function AccountForm() {
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
const [showNewPassword, setShowNewPassword] = useState(false);

const toggleCurrentPasswordVisibility = () => {
setShowCurrentPassword(!showCurrentPassword);
};

const toggleConfirmPasswordVisibility = () => {
setShowNewPassword(!showNewPassword);
};

const { t } = useTranslation();
const user = useSelector((state) => state.user);
const dispatch = useDispatch();
Expand Down Expand Up @@ -134,14 +146,32 @@ function AccountForm() {
<label htmlFor="current password" className="form__label">
{t('AccountForm.CurrentPassword')}
</label>
<input
className="form__input"
aria-label={t('AccountForm.CurrentPasswordARIA')}
type="password"
id="currentPassword"
autoComplete="current-password"
{...field.input}
/>
<div className="password-input-container">
<input
className="form__input"
aria-label={t('AccountForm.CurrentPasswordARIA')}
type={showCurrentPassword ? 'text' : 'password'}
id="currentPassword"
autoComplete="current-password"
{...field.input}
/>
<span
role="button"
tabIndex="0"
className="show-hide-button"
onClick={toggleCurrentPasswordVisibility}
onKeyDown={(e) => {
if (e.key === 'Enter') {
toggleCurrentPasswordVisibility();
}
}}
>
{showCurrentPassword
? t('AccountForm.HidePassword')
: t('AccountForm.ShowPassword')}
</span>
</div>

{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
Expand All @@ -154,14 +184,32 @@ function AccountForm() {
<label htmlFor="new password" className="form__label">
{t('AccountForm.NewPassword')}
</label>
<input
className="form__input"
aria-label={t('AccountForm.NewPasswordARIA')}
type="password"
id="newPassword"
autoComplete="new-password"
{...field.input}
/>
<div className="password-input-container">
<input
className="form__input"
aria-label={t('AccountForm.NewPasswordARIA')}
type={showNewPassword ? 'text' : 'password'}
id="newPassword"
autoComplete="new-password"
{...field.input}
/>
<span
role="button"
tabIndex="0"
className="show-hide-button"
onClick={toggleConfirmPasswordVisibility}
onKeyDown={(e) => {
if (e.key === 'Enter') {
toggleConfirmPasswordVisibility();
}
}}
>
{showNewPassword
? t('AccountForm.HidePassword')
: t('AccountForm.ShowPassword')}
</span>
</div>

{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
Expand Down
58 changes: 42 additions & 16 deletions client/modules/User/components/LoginForm.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Form, Field } from 'react-final-form';
import { useDispatch } from 'react-redux';
Expand All @@ -7,13 +8,19 @@ import { validateLogin } from '../../../utils/reduxFormUtils';
import { validateAndLoginUser } from '../actions';

function LoginForm() {
const [showPassword, setShowPassword] = useState(false);

const { t } = useTranslation();

const dispatch = useDispatch();
function onSubmit(formProps) {
return dispatch(validateAndLoginUser(formProps));
}

const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};

return (
<Form
fields={['email', 'password']}
Expand Down Expand Up @@ -44,22 +51,41 @@ function LoginForm() {
</Field>
<Field name="password">
{(field) => (
<p className="form__field">
<label htmlFor="password" className="form__label">
{t('LoginForm.Password')}
</label>
<input
className="form__input"
aria-label={t('LoginForm.PasswordARIA')}
type="password"
id="password"
autoComplete="current-password"
{...field.input}
/>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
<>
<p className="form__field">
<label htmlFor="password" className="form__label">
{t('LoginForm.Password')}
</label>
<div className="password-input-container">
<input
className="form__input"
aria-label={t('LoginForm.PasswordARIA')}
type={showPassword ? 'text' : 'password'}
id="password"
autoComplete="current-password"
{...field.input}
/>
<span
role="button"
tabIndex="0"
className="show-hide-button"
onClick={togglePasswordVisibility}
onKeyDown={(e) => {
if (e.key === 'Enter') {
togglePasswordVisibility();
}
}}
>
{showPassword
? t('LoginForm.HidePassword')
: t('LoginForm.ShowPassword')}
</span>
</div>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
</p>
</>
)}
</Field>
{submitError && !modifiedSinceLastSubmit && (
Expand Down
79 changes: 63 additions & 16 deletions client/modules/User/components/SignupForm.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Form, Field } from 'react-final-form';
import { useDispatch } from 'react-redux';
Expand Down Expand Up @@ -33,6 +34,17 @@ function validateEmail(email) {
}

function SignupForm() {
const [showPassword, setShowPassword] = useState(false);
const [ConfirmShowPassword, setConfirmShowPassword] = useState(false);

const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};

const toggleConfirmPasswordVisibility = () => {
setConfirmShowPassword(!ConfirmShowPassword);
};

const { t } = useTranslation();

const dispatch = useDispatch();
Expand Down Expand Up @@ -98,14 +110,32 @@ function SignupForm() {
<label htmlFor="password" className="form__label">
{t('SignupForm.Password')}
</label>
<input
className="form__input"
aria-label={t('SignupForm.PasswordARIA')}
type="password"
id="password"
autoComplete="new-password"
{...field.input}
/>
<div className="password-input-container">
<input
className="form__input"
aria-label={t('SignupForm.PasswordARIA')}
type={showPassword ? 'text' : 'password'}
id="password"
autoComplete="new-password"
{...field.input}
/>
<span
role="button"
tabIndex="0"
className="show-hide-button"
onClick={togglePasswordVisibility}
onKeyDown={(e) => {
if (e.key === 'Enter') {
togglePasswordVisibility();
}
}}
>
{showPassword
? t('SignupForm.HidePassword')
: t('SignupForm.ShowPassword')}
</span>
</div>

{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
Expand All @@ -118,14 +148,31 @@ function SignupForm() {
<label htmlFor="confirm password" className="form__label">
{t('SignupForm.ConfirmPassword')}
</label>
<input
className="form__input"
type="password"
aria-label={t('SignupForm.ConfirmPasswordARIA')}
id="confirm password"
autoComplete="new-password"
{...field.input}
/>
<div className="password-input-container">
<input
className="form__input"
type={ConfirmShowPassword ? 'text' : 'password'}
aria-label={t('SignupForm.ConfirmPasswordARIA')}
id="confirm password"
autoComplete="new-password"
{...field.input}
/>
<span
role="button"
tabIndex="0"
className="show-hide-button"
onClick={toggleConfirmPasswordVisibility}
onKeyDown={(e) => {
if (e.key === 'Enter') {
toggleConfirmPasswordVisibility();
}
}}
>
{ConfirmShowPassword
? t('SignupForm.HidePassword')
: t('SignupForm.ShowPassword')}
</span>
</div>
{field.meta.touched && field.meta.error && (
<span className="form-error">{field.meta.error}</span>
)}
Expand Down
22 changes: 22 additions & 0 deletions client/styles/components/_forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,25 @@
// .form--inline [type="submit"][disabled] {
// cursor: not-allowed;
// }

.password-input-container {
position: relative;
}

.form__input {
padding-right: 35px;
}

.show-hide-button {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
border: none;
cursor: pointer;
outline: none;
}

:focus-visible {
outline-color: lightgreen;
}
12 changes: 9 additions & 3 deletions translations/locales/en-US/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@
"UsernameOrEmailARIA": "Email or Username",
"Password": "Password",
"PasswordARIA": "Password",
"Submit": "Log In"
"Submit": "Log In",
"ShowPassword": "Show",
"HidePassword": "Hide"
},
"LoginView": {
"Title": "p5.js Web Editor | Login",
Expand Down Expand Up @@ -311,7 +313,9 @@
"CurrentPasswordARIA": "Current Password",
"NewPassword": "New Password",
"NewPasswordARIA": "New Password",
"SubmitSaveAllSettings": "Save All Settings"
"SubmitSaveAllSettings": "Save All Settings",
"ShowPassword": "Show",
"HidePassword": "Hide"
},
"AccountView": {
"SocialLogin": "Social Login",
Expand Down Expand Up @@ -357,7 +361,9 @@
"PasswordARIA": "password",
"ConfirmPassword": "Confirm Password",
"ConfirmPasswordARIA": "Confirm password",
"SubmitSignup": "Sign Up"
"SubmitSignup": "Sign Up",
"ShowPassword": "Show",
"HidePassword": "Hide"
},
"SignupView": {
"Title": "p5.js Web Editor | Signup",
Expand Down