mirror of https://github.com/zulip/zulip.git
password: Add password limit indicator to form.
Adds a password limit indicator to the password field in both the registration form and the password reset form. This indicator displays the remaining number of characters allowed, helping users comply with the maximum password length requirement. Fixes: #27922.
This commit is contained in:
parent
e0bd3713cc
commit
f28ec726f6
|
@ -143,12 +143,14 @@ Form is validated both client-side using jquery-validation (see signup.js) and s
|
||||||
</div>
|
</div>
|
||||||
{% elif password_required %}
|
{% elif password_required %}
|
||||||
<div class="input-box password-div">
|
<div class="input-box password-div">
|
||||||
|
<div class="password-label-container">
|
||||||
|
<span><label for="id_password">{{ _('Password') }}</label></span>
|
||||||
|
<span id="password-limit-indicator" class="limit-indicator" data-tippy-content="{{ _('Maximum password length: 100 characters') }}" data-tippy-placement="top"></span>
|
||||||
|
</div>
|
||||||
<input id="id_password" class="required" type="password" name="password" autocomplete="new-password"
|
<input id="id_password" class="required" type="password" name="password" autocomplete="new-password"
|
||||||
value="{% if form.password.value() %}{{ form.password.value() }}{% endif %}"
|
value="{% if form.password.value() %}{{ form.password.value() }}{% endif %}"
|
||||||
maxlength="{{ MAX_PASSWORD_LENGTH }}"
|
|
||||||
data-min-length="{{password_min_length}}"
|
data-min-length="{{password_min_length}}"
|
||||||
data-min-guesses="{{password_min_guesses}}" required />
|
data-min-guesses="{{password_min_guesses}}" required />
|
||||||
<label for="id_password" class="inline-block">{{ _('Password') }}</label>
|
|
||||||
<i class="fa fa-eye-slash password_visibility_toggle" role="button" tabindex="0"></i>
|
<i class="fa fa-eye-slash password_visibility_toggle" role="button" tabindex="0"></i>
|
||||||
{% if full_name %}
|
{% if full_name %}
|
||||||
<span class="help-inline">
|
<span class="help-inline">
|
||||||
|
@ -252,7 +254,7 @@ Form is validated both client-side using jquery-validation (see signup.js) and s
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="register-button-box">
|
<div class="register-button-box">
|
||||||
<button class="register-button" type="submit">
|
<button class="register-button" id="signup-button" type="submit">
|
||||||
<span>{{ _('Sign up') }}</span>
|
<span>{{ _('Sign up') }}</span>
|
||||||
<object class="loader" type="image/svg+xml" data="{{ static('images/loading/loader-white.svg') }}"></object>
|
<object class="loader" type="image/svg+xml" data="{{ static('images/loading/loader-white.svg') }}"></object>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -28,10 +28,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-box password-div">
|
<div class="input-box password-div">
|
||||||
<label for="id_new_password1" class="">{{ _('Password') }}</label>
|
<div class="password-label-container">
|
||||||
|
<span><label for="id_new_password1" class="">{{ _('Password') }}</label></span>
|
||||||
|
<span id="reset-password-limit-indicator" class="limit-indicator" data-tippy-content="{{ _('Maximum password length: 100 characters') }}" data-tippy-placement="top"></span>
|
||||||
|
</div>
|
||||||
<input id="id_new_password1" class="required" type="password" name="new_password1" autocomplete="new-password"
|
<input id="id_new_password1" class="required" type="password" name="new_password1" autocomplete="new-password"
|
||||||
value="{% if form.new_password1.value() %}{{ form.new_password1.value() }}{% endif %}"
|
value="{% if form.new_password1.value() %}{{ form.new_password1.value() }}{% endif %}"
|
||||||
maxlength="100"
|
|
||||||
data-min-length="{{password_min_length}}"
|
data-min-length="{{password_min_length}}"
|
||||||
data-min-guesses="{{password_min_guesses}}" autofocus required />
|
data-min-guesses="{{password_min_guesses}}" autofocus required />
|
||||||
<i class="fa fa-eye-slash password_visibility_toggle" role="button" tabindex="0"></i>
|
<i class="fa fa-eye-slash password_visibility_toggle" role="button" tabindex="0"></i>
|
||||||
|
@ -52,7 +54,7 @@
|
||||||
<label for="id_new_password2" class="">{{ _('Confirm password') }}</label>
|
<label for="id_new_password2" class="">{{ _('Confirm password') }}</label>
|
||||||
<input id="id_new_password2" class="required" type="password" name="new_password2" autocomplete="off"
|
<input id="id_new_password2" class="required" type="password" name="new_password2" autocomplete="off"
|
||||||
value="{% if form.new_password2.value() %}{{ form.new_password2.value() }}{% endif %}"
|
value="{% if form.new_password2.value() %}{{ form.new_password2.value() }}{% endif %}"
|
||||||
maxlength="100" required />
|
required />
|
||||||
<i class="fa fa-eye-slash password_visibility_toggle" role="button" tabindex="0"></i>
|
<i class="fa fa-eye-slash password_visibility_toggle" role="button" tabindex="0"></i>
|
||||||
{% if form.new_password2.errors %}
|
{% if form.new_password2.errors %}
|
||||||
{% for error in form.new_password2.errors %}
|
{% for error in form.new_password2.errors %}
|
||||||
|
@ -63,7 +65,7 @@
|
||||||
|
|
||||||
<div class="input-box m-t-30">
|
<div class="input-box m-t-30">
|
||||||
<div class="centered-button">
|
<div class="centered-button">
|
||||||
<button type="submit" class="" value="Submit">Submit</button>
|
<button type="submit" id="reset-button" class="" value="Submit">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -6,3 +6,4 @@ import "../portico/tippyjs.ts";
|
||||||
import "../../third/bootstrap/css/bootstrap.portico.css";
|
import "../../third/bootstrap/css/bootstrap.portico.css";
|
||||||
import "../../styles/portico/portico_styles.css";
|
import "../../styles/portico/portico_styles.css";
|
||||||
import "tippy.js/dist/tippy.css";
|
import "tippy.js/dist/tippy.css";
|
||||||
|
import "../../styles/app_variables.css";
|
||||||
|
|
|
@ -2,6 +2,7 @@ import $ from "jquery";
|
||||||
import assert from "minimalistic-assert";
|
import assert from "minimalistic-assert";
|
||||||
import {z} from "zod";
|
import {z} from "zod";
|
||||||
|
|
||||||
|
import render_compose_limit_indicator from "../../templates/compose_limit_indicator.hbs";
|
||||||
import * as common from "../common.ts";
|
import * as common from "../common.ts";
|
||||||
import {$t} from "../i18n.ts";
|
import {$t} from "../i18n.ts";
|
||||||
import {password_quality, password_warning} from "../password_quality.ts";
|
import {password_quality, password_warning} from "../password_quality.ts";
|
||||||
|
@ -342,3 +343,49 @@ $(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $password_elem: JQuery<HTMLInputElement> = $("input#id_password");
|
||||||
|
const $new_password1_elem: JQuery<HTMLInputElement> = $("input#id_new_password1");
|
||||||
|
|
||||||
|
if ($password_elem.length) {
|
||||||
|
$password_elem.on("input", () => {
|
||||||
|
check_overflow_password($password_elem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($new_password1_elem.length) {
|
||||||
|
$new_password1_elem.on("input", () => {
|
||||||
|
check_overflow_password($new_password1_elem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function check_overflow_password($password_elem: JQuery<HTMLInputElement>): void {
|
||||||
|
const password = $password_elem.val() ?? "";
|
||||||
|
const max_length = 100;
|
||||||
|
const remaining_characters = max_length - password.length;
|
||||||
|
|
||||||
|
const $indicator = $password_elem.closest(".input-box").find(".limit-indicator");
|
||||||
|
const $button = $password_elem.closest("form").find("button[type='submit']");
|
||||||
|
|
||||||
|
const password_too_long = password.length > max_length;
|
||||||
|
$button.prop("disabled", password_too_long);
|
||||||
|
|
||||||
|
// Update the limit indicator
|
||||||
|
if (password_too_long) {
|
||||||
|
$indicator.addClass("over_limit");
|
||||||
|
$indicator.html(
|
||||||
|
render_compose_limit_indicator({
|
||||||
|
remaining_characters,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else if (remaining_characters <= 20) {
|
||||||
|
$indicator.removeClass("over_limit");
|
||||||
|
$indicator.html(
|
||||||
|
render_compose_limit_indicator({
|
||||||
|
remaining_characters,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$indicator.text("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1461,3 +1461,34 @@ button#register_auth_button_gitlab {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#signup-button,
|
||||||
|
#reset-button {
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#reset-password-limit-indicator,
|
||||||
|
#password-limit-indicator {
|
||||||
|
&:not(:empty) {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--color-limit-indicator);
|
||||||
|
cursor: pointer;
|
||||||
|
height: 0;
|
||||||
|
margin-right: 5px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.over_limit {
|
||||||
|
color: var(--color-limit-indicator-over-limit);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-label-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue