diff --git a/warehouse/static/js/warehouse/controllers/password_strength_gauge_controller.js b/warehouse/static/js/warehouse/controllers/password_strength_gauge_controller.js new file mode 100644 index 000000000000..9aaa82fe434c --- /dev/null +++ b/warehouse/static/js/warehouse/controllers/password_strength_gauge_controller.js @@ -0,0 +1,41 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* global zxcvbn */ + +import { Controller } from "stimulus"; + +export default class extends Controller { + static targets = ["password", "strengthGauge"]; + + checkPasswordStrength() { + let password = this.passwordTarget.value; + if (password === "") { + this.strengthGaugeTarget.setAttribute("class", "password-strength__gauge"); + this.setScreenReaderMessage("Password field is empty"); + } else { + // following recommendations on the zxcvbn JS docs + // the zxcvbn function is available by loading `vendor/zxcvbn.js` + // in the register, account and reset password templates + let zxcvbnResult = zxcvbn(password); + this.strengthGaugeTarget.setAttribute("class", `password-strength__gauge password-strength__gauge--${zxcvbnResult.score}`); + this.strengthGaugeTarget.setAttribute("data-zxcvbn-score", zxcvbnResult.score); + this.setScreenReaderMessage(zxcvbnResult.feedback.suggestions.join(" ") || "Password is strong"); + } + } + + setScreenReaderMessage(msg) { + this.strengthGaugeTarget.querySelector(".sr-only").innerHTML = msg; + } +} diff --git a/warehouse/static/js/warehouse/utils/forms.js b/warehouse/static/js/warehouse/utils/forms.js index 2f317e9a8376..811a3419de48 100644 --- a/warehouse/static/js/warehouse/utils/forms.js +++ b/warehouse/static/js/warehouse/utils/forms.js @@ -50,15 +50,17 @@ export function submitTriggers() { } } -/* global zxcvbn */ - const tooltipClasses = ["tooltipped", "tooltipped-s", "tooltipped-immediate"]; let passwordFormRoot = document; -const passwordStrengthValidator = (value) => { - const zxcvbnResult = zxcvbn(value); - return zxcvbnResult.score < 2 ? - zxcvbnResult.feedback.suggestions.join(" ") : null; +const passwordStrengthValidator = () => { + let passwordGauge = document.querySelector(".password-strength__gauge"); + let score = parseInt(passwordGauge.getAttribute("data-zxcvbn-score")); + if (!isNaN(score) && score < 2) { + return passwordGauge.querySelector(".sr-only").innerHTML; + } else { + return null; + } }; const fieldRequiredValidator = (value) => { @@ -66,34 +68,6 @@ const fieldRequiredValidator = (value) => { "Please fill out this field" : null; }; -const checkPasswordStrength = (event) => { - let result = passwordFormRoot.querySelector(".password-strength__gauge"); - if (event.target.value === "") { - result.setAttribute("class", "password-strength__gauge"); - // Feedback for screen readers - result.querySelector(".sr-only").innerHTML = "Password field is empty"; - } else { - // following recommendations on the zxcvbn JS docs - // the zxcvbn function is available by loading `vendor/zxcvbn.js` - // in the register page template only - let zxcvbnResult = zxcvbn(event.target.value); - result.setAttribute("class", `password-strength__gauge password-strength__gauge--${zxcvbnResult.score}`); - - // Feedback for screen readers - result.querySelector(".sr-only").innerHTML = zxcvbnResult.feedback.suggestions.join(" ") || "Password is strong"; - } -}; - -const setupPasswordStrengthGauge = () => { - let password = passwordFormRoot.querySelector("#new_password"); - if (password === null) return; - password.addEventListener( - "input", - checkPasswordStrength, - false - ); -}; - const attachTooltip = (field, message) => { let parentNode = field.parentNode; parentNode.classList.add.apply(parentNode.classList, tooltipClasses); @@ -121,14 +95,6 @@ const validateForm = (event) => { } let password = passwordFormRoot.querySelector("#new_password"); - let passwordConfirm = passwordFormRoot.querySelector("#password_confirm"); - if (password.value !== passwordConfirm.value) { - let message = "Passwords do not match"; - attachTooltip(password, message); - event.preventDefault(); - return false; - } - let passwordStrengthMessage = passwordStrengthValidator(password.value); if (passwordStrengthMessage !== null) { attachTooltip(password, passwordStrengthMessage); @@ -138,12 +104,11 @@ const validateForm = (event) => { }; export function registerFormValidation() { - const passwordStrengthNode = document.querySelector(".password-strength"); - if (passwordStrengthNode === null) return; + const newPasswordNode = document.querySelector("#new_password"); + if (newPasswordNode === null) return; passwordFormRoot = document.evaluate( - "./ancestor::form", passwordStrengthNode, null, + "./ancestor::form", newPasswordNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; - setupPasswordStrengthGauge(); const submitButton = passwordFormRoot.querySelector("input[type='submit']"); submitButton.addEventListener("click", validateForm, false); } diff --git a/warehouse/templates/accounts/register.html b/warehouse/templates/accounts/register.html index 6c8284f90269..24ddd54d5b88 100644 --- a/warehouse/templates/accounts/register.html +++ b/warehouse/templates/accounts/register.html @@ -27,7 +27,7 @@
Choose a strong password that contains letters (uppercase and lowercase), numbers and special characters. Avoid common words or repetition.
Password strength:
-
+
Password field is empty
diff --git a/warehouse/templates/manage/account.html b/warehouse/templates/manage/account.html
index 1e07d51587ff..f2002ef646cd 100644
--- a/warehouse/templates/manage/account.html
+++ b/warehouse/templates/manage/account.html
@@ -203,7 +203,7 @@ Account Emails
Change Password
{{ form_error_anchor(change_password_form) }}
-