Skip to content

Conversation

@MrHutmat
Copy link
Contributor

Implemented an inline toggle button to show/hide your password, also changed the css to accommodate these changes, for the login screen

Description

Created methods to add a button for the toggle as well as a method for creating the span element that contains the button.
The button has an SVG attached that changes depending on the toggle state.
The button's onClick event targets the field for the password and changes its type from "password" to "text"
The button only appears if you hover the span or have focus-within in the span element.

Wrapped the password input in a new span element so I could manipulate the input field's length

I have removed some of the styling that wasn't used, as well as added CSS that specifically targets each input element (username and password).

Recording2025-10-22145005mp4-ezgif com-optimize

Copilot AI review requested due to automatic review settings October 22, 2025 13:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a password visibility toggle button for the login screen, allowing users to show/hide their password input. The implementation adds an inline toggle button with SVG icons that appears on hover or focus, and refactors the CSS to support the new layout.

  • Adds a toggle button with eye/eye-off SVG icons that switches between showing and hiding the password
  • Wraps the password input in a span container to accommodate the toggle button
  • Updates CSS to target specific input elements and style the new password input container with toggle button

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/Umbraco.Web.UI.Login/src/auth.element.ts Adds createButton and createShowPasswordToggleItem functions, modifies createFormLayoutItem to optionally include the toggle button, and updates component initialization
src/Umbraco.Web.UI.Login/src/auth-styles.css Refactors CSS to target username and password inputs separately, adds styles for the password container span and toggle button

…changed the css to accommodate these changes
Added the svg's to their own const for easy reuse

Added localization for the arialabel on the button

Seperated the createFormLayoutItem so there is a seperate for the password input

Moved all the conditional logic in the onclick event to fit inside one if/else statement
…enough for localization to load, and replaced it with a function.

The function will try and resolve the promise by checking if the localize.terms methods returns a changed value, if not then it retries every 50ms or untill it hits a max retry of 40/2 seconds.
Copy link
Contributor

@iOvergaard iOvergaard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had said in the past that the real solution was to use the <uui-input-password /> component, which supports this OOTB. However, it is not possible to use that component because many password managers do not support Shadow DOM, which is why we create the input elements in the root and move them in through slots to their final place.
I don't mind adding this to the login screen, but we need to ensure that we support the same browsers as the UI library and that we do not introduce the same bugs again. So I left a comment down below:

@MrHutmat MrHutmat force-pushed the v16/feature/add-show-password-field branch from 9139429 to e294b05 Compare October 23, 2025 14:21
@MrHutmat MrHutmat requested a review from iOvergaard October 24, 2025 10:09
@iOvergaard iOvergaard requested a review from Copilot October 27, 2025 08:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

Comment on lines 57 to 63
const closedEyeSVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"></path>
<path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"></path>
<path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"></path>
<line x1="2" x2="22" y1="2" y2="22"></line>
</svg>
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation between the two SVG definitions. The closedEyeSVG has extra leading tabs compared to openEyeSVG. Align the indentation for consistency.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrHutmat, I think we could benefit a bit by cutting these two SVG codes out into real .svg files, which you then import into the app. Vite will automatically bundle it up based on asset rules and load it most efficiently. This approach also has the added benefit of reducing the number of lines in this file as well as solving this Copilot suggestion.

Comment on lines 218 to 239
async #waitForLocalization(): Promise<void> {
return new Promise((resolve) => {
// Check if localization is already available
if (this.localize.term('auth_showPassword') !== 'auth_showPassword') {
resolve();
return;
}

let retryCount = 0;
//Retries 40 times with a 50ms interval = 2 seconds
const maxRetries = 40;

// If not, we check periodically until it it is available or we reach the max retries
const checkInterval = setInterval(() => {
if (this.localize.term('auth_showPassword') !== 'auth_showPassword' || retryCount >= maxRetries) {
clearInterval(checkInterval);
resolve();
}
retryCount++;
}, 50);
});
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The polling interval runs indefinitely if localization never loads. Consider adding a timeout or rejection mechanism to handle the case where maxRetries is reached without successful localization.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a comment here to consider from Copilot. The logic looks OK to me, so I don't know where it sees that the logic would keep running. I do, however, think we should separate the two checks in the if-sentence into two separate if-sentences, so that you can reject() the Promise if localization is never available. That way, you'll stop the form from rendering.

if (retryCount > maxRetries) {
  clearInterval(checkInterval);
  reject('Localization not available');
  return;
}

if (this.localize.term('auth_showPassword') !== 'auth_showPassword') {
  clearInterval(checkInterval);
  resolve();
  return;
}

retryCount++;

Copy link
Contributor

@iOvergaard iOvergaard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks OK, but based on Copilot feedback and my own findings, I have made two suggestions:

Comment on lines 57 to 63
const closedEyeSVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"></path>
<path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"></path>
<path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"></path>
<line x1="2" x2="22" y1="2" y2="22"></line>
</svg>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrHutmat, I think we could benefit a bit by cutting these two SVG codes out into real .svg files, which you then import into the app. Vite will automatically bundle it up based on asset rules and load it most efficiently. This approach also has the added benefit of reducing the number of lines in this file as well as solving this Copilot suggestion.

Comment on lines 218 to 239
async #waitForLocalization(): Promise<void> {
return new Promise((resolve) => {
// Check if localization is already available
if (this.localize.term('auth_showPassword') !== 'auth_showPassword') {
resolve();
return;
}

let retryCount = 0;
//Retries 40 times with a 50ms interval = 2 seconds
const maxRetries = 40;

// If not, we check periodically until it it is available or we reach the max retries
const checkInterval = setInterval(() => {
if (this.localize.term('auth_showPassword') !== 'auth_showPassword' || retryCount >= maxRetries) {
clearInterval(checkInterval);
resolve();
}
retryCount++;
}, 50);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a comment here to consider from Copilot. The logic looks OK to me, so I don't know where it sees that the logic would keep running. I do, however, think we should separate the two checks in the if-sentence into two separate if-sentences, so that you can reject() the Promise if localization is never available. That way, you'll stop the form from rendering.

if (retryCount > maxRetries) {
  clearInterval(checkInterval);
  reject('Localization not available');
  return;
}

if (this.localize.term('auth_showPassword') !== 'auth_showPassword') {
  clearInterval(checkInterval);
  resolve();
  return;
}

retryCount++;

@iOvergaard
Copy link
Contributor

@MrHutmat > Apart from the suggestions above, I think we should consider rebasing this PR to v17/dev, so that it will land in 17.1. Otherwise, 16.4 will have a feature that will not be available in 17.0. However, I think this branch is too far ahead to rebase it directly, but we can wait to merge it in until v17/dev has been merged into the main branch, which should happen either next week, when 16.4 is built, or in two weeks, when 16.4-rc is released.

Changed the logic for waitForLocallization Added the svg's as files that are imported instead of having the raw svg in the code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants