Toggle visibility of password input fields in login-ftl-based pages
Closes #22067
This commit is contained in:
parent
722422a0cf
commit
86c0e338d9
11 changed files with 209 additions and 92 deletions
|
@ -40,3 +40,35 @@ from the same list of values.
|
|||
|
||||
In this release, the role and client mappers now map to a single value from the effective roles of a user when
|
||||
they are marked as single-valued (`Multivalued` disabled).
|
||||
|
||||
= Changes to password fields in Login UI
|
||||
|
||||
In this version we want to introduce a toggle to hide/show password inputs.
|
||||
|
||||
.Affected pages:
|
||||
- login.ftl
|
||||
- login-password.ftl
|
||||
- login-update-password.ftl
|
||||
- register.ftl
|
||||
- register-user-profile.ftl
|
||||
|
||||
In general all `<input type="password" name="password" />` are encapsulated within a div now. The input element is followed by a button which toggles the visibility of the password input.
|
||||
|
||||
Old code example:
|
||||
[source,html]
|
||||
----
|
||||
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
|
||||
----
|
||||
|
||||
New code example:
|
||||
[source,html]
|
||||
----
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
----
|
||||
|
|
|
@ -284,12 +284,12 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
|||
);
|
||||
Assert.assertTrue(
|
||||
driver.findElement(
|
||||
By.cssSelector("form#kc-register-form > div:nth-child(4) > div:nth-child(2) > input#password")
|
||||
By.cssSelector("#password")
|
||||
).isDisplayed()
|
||||
);
|
||||
Assert.assertTrue(
|
||||
driver.findElement(
|
||||
By.cssSelector("form#kc-register-form > div:nth-child(5) > div:nth-child(2) > input#password-confirm")
|
||||
By.cssSelector("#password-confirm")
|
||||
).isDisplayed()
|
||||
);
|
||||
Assert.assertTrue(
|
||||
|
@ -434,12 +434,12 @@ public class RegisterWithUserProfileTest extends RegisterTest {
|
|||
);
|
||||
Assert.assertTrue(
|
||||
driver.findElement(
|
||||
By.cssSelector("form#kc-register-form > div:nth-child(3) > div:nth-child(2) > input#password")
|
||||
By.cssSelector("#password")
|
||||
).isDisplayed()
|
||||
);
|
||||
Assert.assertTrue(
|
||||
driver.findElement(
|
||||
By.cssSelector("form#"+htmlFormId+" > div:nth-child(4) > div:nth-child(2) > input#password-confirm")
|
||||
By.cssSelector("#password-confirm")
|
||||
).isDisplayed()
|
||||
);
|
||||
Assert.assertTrue(
|
||||
|
|
|
@ -10,10 +10,17 @@
|
|||
<div class="${properties.kcFormGroupClass!} no-bottom-margin">
|
||||
<hr/>
|
||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password"
|
||||
type="password" autocomplete="on" autofocus
|
||||
aria-invalid="<#if messagesPerField.existsError('password')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
${kcSanitize(messagesPerField.get('password'))?no_esc}
|
||||
|
@ -38,6 +45,7 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||
</#if>
|
||||
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -7,17 +7,31 @@
|
|||
<form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
|
||||
<input type="text" id="username" name="username" value="${username}" autocomplete="username"
|
||||
readonly="readonly" style="display:none;"/>
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password" name="password" autocomplete="current-password" style="display:none;"/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div class="${properties.kcLabelWrapperClass!}">
|
||||
<label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}"
|
||||
autofocus autocomplete="new-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password-new" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -32,11 +46,18 @@
|
|||
<label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password-confirm" name="password-confirm"
|
||||
class="${properties.kcInputClass!}"
|
||||
autocomplete="new-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password-confirm" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if messagesPerField.existsError('password-confirm')>
|
||||
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -60,5 +81,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -27,9 +27,16 @@
|
|||
<div class="${properties.kcFormGroupClass!}">
|
||||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="off"
|
||||
aria-invalid="<#if messagesPerField.existsError('username','password')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg("showPassword")}"
|
||||
aria-controls="password" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if usernameHidden?? && messagesPerField.existsError('username','password')>
|
||||
<span id="input-error" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -68,8 +75,8 @@
|
|||
</form>
|
||||
</#if>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||
<#elseif section = "info" >
|
||||
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
|
||||
<div id="kc-registration-container">
|
||||
|
|
|
@ -88,6 +88,8 @@ password=Password
|
|||
passwordConfirm=Confirm password
|
||||
passwordNew=New Password
|
||||
passwordNewConfirm=New Password confirmation
|
||||
hidePassword=Hide password
|
||||
showPassword=Show password
|
||||
rememberMe=Remember me
|
||||
authenticatorCode=One-time code
|
||||
address=Address
|
||||
|
|
|
@ -16,10 +16,17 @@
|
|||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label> *
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
||||
autocomplete="new-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -35,10 +42,17 @@
|
|||
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label> *
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
||||
name="password-confirm"
|
||||
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password-confirm" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if messagesPerField.existsError('password-confirm')>
|
||||
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -73,5 +87,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -85,10 +85,18 @@
|
|||
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password" class="${properties.kcInputClass!}" name="password"
|
||||
autocomplete="new-password"
|
||||
aria-invalid="<#if messagesPerField.existsError('password','password-confirm')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<#if messagesPerField.existsError('password')>
|
||||
<span id="input-error-password" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -104,10 +112,17 @@
|
|||
class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
|
||||
</div>
|
||||
<div class="${properties.kcInputWrapperClass!}">
|
||||
<div class="${properties.kcInputGroup!}">
|
||||
<input type="password" id="password-confirm" class="${properties.kcInputClass!}"
|
||||
name="password-confirm"
|
||||
aria-invalid="<#if messagesPerField.existsError('password-confirm')>true</#if>"
|
||||
/>
|
||||
<button class="pf-c-button pf-m-control" type="button" aria-label="${msg('showPassword')}"
|
||||
aria-controls="password-confirm" data-password-toggle
|
||||
data-label-show="${msg('showPassword')}" data-label-hide="${msg('hidePassword')}">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<#if messagesPerField.existsError('password-confirm')>
|
||||
<span id="input-error-password-confirm" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
|
||||
|
@ -140,5 +155,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
|
||||
</#if>
|
||||
</@layout.registrationLayout>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
const toggle = (button) => {
|
||||
const passwordElement = document.getElementById(button.getAttribute('aria-controls'));
|
||||
if (passwordElement.type === "password") {
|
||||
passwordElement.type = "text";
|
||||
button.children.item(0).classList.replace("fa-eye", "fa-eye-slash");
|
||||
button.setAttribute("aria-label", button.dataset.labelHide);
|
||||
} else if(passwordElement.type === "text") {
|
||||
passwordElement.type = "password";
|
||||
button.children.item(0).classList.replace("fa-eye-slash", "fa-eye");
|
||||
button.setAttribute("aria-label", button.dataset.labelShow);
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-password-toggle]')
|
||||
.forEach(button => button.onclick = () => toggle(button));
|
|
@ -24,7 +24,6 @@ p.instruction {
|
|||
}
|
||||
|
||||
.pf-c-button.pf-m-control {
|
||||
border: solid var(--pf-global--BorderWidth--sm);
|
||||
border-color: rgba(230, 230, 230, 0.5);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ kcInputClassCheckboxInput=pf-c-check__input
|
|||
kcInputClassCheckboxLabel=pf-c-check__label
|
||||
kcInputClassRadioCheckboxLabelDisabled=pf-m-disabled
|
||||
kcInputErrorMessageClass=pf-c-form__helper-text pf-m-error required kc-feedback-text
|
||||
kcInputGroup=pf-c-input-group
|
||||
kcInputWrapperClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
|
||||
kcFormOptionsClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
|
||||
kcFormButtonsClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
|
||||
|
|
Loading…
Reference in a new issue