UX: refactor password reset page (#30323)

This commit is contained in:
Jordan Vidrine 2024-12-17 12:11:02 -06:00 committed by GitHub
parent af8c98217a
commit 8f26ae7b7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 128 additions and 108 deletions

View File

@ -29,6 +29,7 @@ export default class PasswordResetController extends Controller.extend(
requiresApproval = false;
redirected = false;
maskPassword = true;
passwordValidationVisible = false;
lockImageUrl = getURL("/images/lock.svg");
@ -65,6 +66,30 @@ export default class PasswordResetController extends Controller.extend(
return getURL(redirectTo || "/");
}
@discourseComputed(
"passwordValidation.ok",
"passwordValidation.reason",
"passwordValidationVisible"
)
showPasswordValidation(
passwordValidationOk,
passwordValidationReason,
passwordValidationVisible
) {
return (
passwordValidationOk ||
(passwordValidationReason && passwordValidationVisible)
);
}
@action
togglePasswordValidation() {
this.set(
"passwordValidationVisible",
Boolean(this.passwordValidation.reason)
);
}
@action
done(event) {
if (wantsNewWindow(event)) {

View File

@ -2,11 +2,7 @@
{{hide-application-sidebar}}
{{hide-application-header-buttons "search" "login" "signup" "menu"}}
<div class="container password-reset clearfix">
<div class="pull-left col-image">
<img src={{this.lockImageUrl}} class="password-reset-img" alt="" />
</div>
<div class="pull-left col-form">
<form class="change-password-form login-left-side">
{{#if this.successMessage}}
<p>{{this.successMessage}}</p>
@ -22,92 +18,95 @@
{{/unless}}
{{/if}}
{{else}}
<form class="change-password-form">
{{#if this.securityKeyOrSecondFactorRequired}}
<h2>{{i18n "user.change_password.title"}}</h2>
<p>
{{i18n "user.change_password.verify_identity"}}
</p>
{{#if this.errorMessage}}
<div class="alert alert-error">{{this.errorMessage}}</div>
<br />
{{/if}}
{{#if this.securityKeyOrSecondFactorRequired}}
<h2>{{i18n "user.change_password.title"}}</h2>
<p>
{{i18n "user.change_password.verify_identity"}}
</p>
{{#if this.errorMessage}}
<div class="alert alert-error">{{this.errorMessage}}</div>
<br />
{{/if}}
{{#if this.displaySecurityKeyForm}}
<SecurityKeyForm
@setSecondFactorMethod={{fn
(mut this.selectedSecondFactorMethod)
}}
@backupEnabled={{this.backupEnabled}}
@totpEnabled={{this.secondFactorRequired}}
@otherMethodAllowed={{this.otherMethodAllowed}}
@action={{this.authenticateSecurityKey}}
/>
{{else}}
<SecondFactorForm
@secondFactorMethod={{this.selectedSecondFactorMethod}}
@secondFactorToken={{this.secondFactorToken}}
@backupEnabled={{this.backupEnabled}}
@totpEnabled={{this.secondFactorRequired}}
@isLogin={{false}}
>
<SecondFactorInput
{{on
"input"
(with-event-value (fn (mut this.secondFactorToken)))
}}
@secondFactorMethod={{this.selectedSecondFactorMethod}}
value={{this.secondFactorToken}}
id="second-factor"
/>
</SecondFactorForm>
{{/if}}
{{#unless this.displaySecurityKeyForm}}
<DButton
@action={{action "submit"}}
@label="submit"
type="submit"
class="btn-primary"
/>
{{/unless}}
{{#if this.displaySecurityKeyForm}}
<SecurityKeyForm
@setSecondFactorMethod={{fn (mut this.selectedSecondFactorMethod)}}
@backupEnabled={{this.backupEnabled}}
@totpEnabled={{this.secondFactorRequired}}
@otherMethodAllowed={{this.otherMethodAllowed}}
@action={{this.authenticateSecurityKey}}
/>
{{else}}
<h2>{{i18n "user.change_password.choose"}}</h2>
{{#if this.errorMessage}}
<div class="alert alert-error">{{this.errorMessage}}</div>
<br />
{{/if}}
<div class="input">
<PasswordField
@value={{this.accountPassword}}
@capsLockOn={{this.capsLockOn}}
type={{if this.maskPassword "password" "text"}}
autofocus="autofocus"
autocomplete="new-password"
id="new-account-password"
<SecondFactorForm
@secondFactorMethod={{this.selectedSecondFactorMethod}}
@secondFactorToken={{this.secondFactorToken}}
@backupEnabled={{this.backupEnabled}}
@totpEnabled={{this.secondFactorRequired}}
@isLogin={{false}}
>
<SecondFactorInput
{{on
"input"
(with-event-value (fn (mut this.secondFactorToken)))
}}
@secondFactorMethod={{this.selectedSecondFactorMethod}}
value={{this.secondFactorToken}}
id="second-factor"
/>
</SecondFactorForm>
{{/if}}
{{#unless this.displaySecurityKeyForm}}
<DButton
@action={{action "submit"}}
@label="submit"
type="submit"
class="btn-primary"
/>
{{/unless}}
{{else}}
<h2>{{i18n "user.change_password.choose_new"}}</h2>
{{#if this.errorMessage}}
<div class="alert alert-error">{{this.errorMessage}}</div>
<br />
{{/if}}
<div class="input">
<PasswordField
@value={{this.accountPassword}}
{{on "focusout" this.togglePasswordValidation}}
@capsLockOn={{this.capsLockOn}}
type={{if this.maskPassword "password" "text"}}
autofocus="autofocus"
autocomplete="new-password"
id="new-account-password"
/>
<div class="change-password__password-info">
<div class="change-password_tip-validation">
{{#if this.showPasswordValidation}}
<InputTip @validation={{this.passwordValidation}} />
{{/if}}
<div
class="caps-lock-warning {{unless this.capsLockOn 'hidden'}}"
>
{{d-icon "triangle-exclamation"}}
{{i18n "login.caps_lock_warning"}}
</div>
</div>
<TogglePasswordMask
@maskPassword={{this.maskPassword}}
@togglePasswordMask={{this.togglePasswordMask}}
/>
<div class="caps-lock-warning {{unless this.capsLockOn 'hidden'}}">
{{d-icon "triangle-exclamation"}}
{{i18n "login.caps_lock_warning"}}
</div>
</div>
</div>
<InputTip @validation={{this.passwordValidation}} />
<DButton
@action={{action "submit"}}
@label="user.change_password.set_password"
type="submit"
class="btn-primary"
/>
{{/if}}
</form>
<DButton
@action={{action "submit"}}
@label="user.change_password.set_password"
type="submit"
class="btn-primary"
/>
{{/if}}
{{/if}}
</div>
</form>
</div>

View File

@ -1,4 +1,4 @@
import { click, fillIn, visit } from "@ember/test-helpers";
import { blur, click, fillIn, visit } from "@ember/test-helpers";
import { test } from "qunit";
import sinon from "sinon";
import PreloadStore from "discourse/lib/preload-store";
@ -71,6 +71,8 @@ acceptance("Password Reset", function (needs) {
assert.dom(".password-reset .tip.good").exists("input looks good");
await fillIn(".password-reset input", "123");
await blur(".password-reset input");
assert.dom(".password-reset .tip.bad").exists("input is not valid");
assert.dom(".password-reset .tip.bad").includesHtml(
i18n("user.password.too_short", {

View File

@ -11,7 +11,8 @@
body.login-page,
body.signup-page,
body.invite-page {
body.invite-page,
body.password-reset-page {
& ~ .powered-by-discourse,
.above-main-container-outlet {
display: none;
@ -25,7 +26,8 @@ body.signup-page {
.login-fullpage,
.signup-fullpage,
.invites-show {
.invites-show,
.password-reset-page {
.signup-body,
.login-body {
display: flex;
@ -241,8 +243,6 @@ body.signup-page {
.caps-lock-warning {
color: var(--danger);
font-size: var(--font-down-1);
font-weight: bold;
margin-top: 0.5em;
}
.create-account__password-info {

View File

@ -33,28 +33,22 @@ body.invite-page {
// the second button can wrap in some locales, and this helps alignment
}
.password-reset {
.instructions {
label {
color: var(--primary-medium);
}
}
#new-account-password {
width: 15em;
}
.tip {
margin: 0 0 0.5em;
}
.toggle-password-mask {
margin-left: 0.25em;
}
}
.password-reset-page {
.caps-lock-warning {
display: inline;
}
.change-password-form {
margin: 0 auto;
display: flex;
flex-direction: column;
width: 400px;
input {
padding: 0.75em 0.77em;
min-width: 250px;
margin-bottom: 0.25em;
width: 100%;
}
.input {
position: relative;
margin-bottom: 1em;
}
.tip {
display: block;
}