Skip to content

Commit

Permalink
Merge pull request #15605 from joeyksclark/password-show-hide-14321
Browse files Browse the repository at this point in the history
Add password visibility toggle
  • Loading branch information
ornicar committed Jun 26, 2024
2 parents 223488a + 2de5624 commit 2ae4c6e
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 21 deletions.
2 changes: 1 addition & 1 deletion modules/team/src/main/ui/RequestUi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class RequestUi(helpers: Helpers, bits: TeamUi):
)
),
t.password.nonEmpty.so(
form3.passwordModified(form("password"), trt.entryCode())(
form3.passwordModified(form("password"), trt.entryCode(), reveal = false)(
autocomplete := "new-password"
)
),
Expand Down
10 changes: 8 additions & 2 deletions modules/ui/src/main/helper/Form3.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,14 @@ final class Form3(formHelper: FormHelper & I18nHelper, flairApi: FlairApi):
// allows disabling of a field that defaults to true
def hiddenFalse(field: Field): Tag = hidden(field, "false".some)

def passwordModified(field: Field, content: Frag)(modifiers: Modifier*)(using Translate): Frag =
group(field, content)(input(_, typ = "password")(required)(modifiers))
def passwordModified(field: Field, content: Frag, reveal: Boolean = true)(
modifiers: Modifier*
)(using Translate): Frag =
group(field, content): f =>
div(cls := "password-wrapper")(
input(f, typ = "password")(required)(modifiers),
reveal.option(button(cls := "password-reveal", dataIcon := Icon.Eye))
)

def passwordComplexityMeter(labelContent: Frag): Frag =
div(cls := "password-complexity")(
Expand Down
1 change: 1 addition & 0 deletions ui/bits/css/build/bits.account.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
@import '../../../common/css/form/form3';
@import '../../../common/css/form/radio';
@import '../../../common/css/form/emoji-picker';
@import '../../../common/css/form/password';
@import '../account';
1 change: 1 addition & 0 deletions ui/bits/css/build/bits.auth.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import '../../../common/css/plugin';
@import '../../../common/css/form/form3';
@import '../../../common/css/form/captcha';
@import '../../../common/css/form/password';
@import '../auth';
3 changes: 3 additions & 0 deletions ui/bits/src/bits.account.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import * as licon from 'common/licon';
import * as xhr from 'common/xhr';
import { addPasswordVisibilityToggleListener } from 'common/password';
import flairPicker from './load/flairPicker';

site.load.then(() => {
$('.emoji-details').each(function (this: HTMLElement) {
flairPicker(this);
});

addPasswordVisibilityToggleListener();

const localPrefs: [string, string, string, boolean][] = [
['behavior', 'arrowSnap', 'arrow.snap', true],
['behavior', 'courtesy', 'courtesy', false],
Expand Down
4 changes: 3 additions & 1 deletion ui/bits/src/bits.login.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import * as xhr from 'common/xhr';
import debounce from 'common/debounce';
import { addPasswordVisibilityToggleListener } from 'common/password';
import { storedJsonProp } from 'common/storage';

export function initModule(mode: 'login' | 'signup') {
mode === 'login' ? loginStart() : signupStart();

addPasswordVisibilityToggleListener();
}
class LoginHistory {
historyStorage = storedJsonProp<number[]>('login.history', () => []);
Expand All @@ -25,7 +28,6 @@ function loginStart() {

const toggleSubmit = ($submit: Cash, v: boolean) =>
$submit.prop('disabled', !v).toggleClass('disabled', !v);

(function load() {
const form = document.querySelector(selector) as HTMLFormElement,
$f = $(form);
Expand Down
17 changes: 0 additions & 17 deletions ui/common/css/form/_form3.scss
Original file line number Diff line number Diff line change
Expand Up @@ -114,23 +114,6 @@ textarea.form-control {
border-top: $border;
}

.password-complexity {
margin-top: -2rem;
margin-bottom: 3rem;
}

.password-complexity-meter {
display: flex;
grid-gap: 0.25rem;
height: 0.4rem;
margin-top: 1rem;

> * {
background-color: gray;
width: 25%;
}
}

.form-fieldset {
@extend %box-radius;
margin: 1rem 0 3rem 0;
Expand Down
25 changes: 25 additions & 0 deletions ui/common/css/form/_password.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.password-complexity {
margin-top: -2rem;
margin-bottom: 3rem;
}
.password-complexity-meter {
display: flex;
grid-gap: 0.25rem;
height: 0.4rem;
margin-top: 1rem;

> * {
background-color: gray;
width: 25%;
}
}

.password-reveal {
@extend %button-none;
float: right;
margin-right: 1em;
margin-top: -2.2em;
}
.password-reveal.revealed {
color: $c-bad;
}
13 changes: 13 additions & 0 deletions ui/common/src/password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const addPasswordVisibilityToggleListener = () => {
$('.password-wrapper').each(function (this: HTMLElement) {
const $wrapper = $(this);
const $button = $wrapper.find('.password-reveal');
$button.on('click', function (e: Event) {
e.preventDefault();
const $input = $wrapper.find('input');
const type = $input.attr('type') === 'password' ? 'text' : 'password';
$input.attr('type', type);
$button.toggleClass('revealed', type == 'text');
});
});
};

0 comments on commit 2ae4c6e

Please sign in to comment.