[KEYCLOAK-17166] Use radio buttons for otp select

This commit is contained in:
Bodo Graumann 2021-02-18 18:20:35 +01:00 committed by Marek Posolda
parent a60cb65252
commit 0033b7daf7
4 changed files with 71 additions and 84 deletions

View file

@ -89,7 +89,7 @@ public class LoginTotpPage extends LanguageComboboxAwarePage {
// If false, we don't expect that credentials combobox is available. If true, we expect that it is available on the page
public void assertOtpCredentialSelectorAvailability(boolean expectedAvailability) {
try {
driver.findElement(By.className("otp-tile"));
driver.findElement(By.className("pf-c-tile"));
Assert.assertTrue(expectedAvailability);
} catch (NoSuchElementException nse) {
Assert.assertFalse(expectedAvailability);
@ -105,7 +105,7 @@ public class LoginTotpPage extends LanguageComboboxAwarePage {
public String getSelectedOtpCredential() {
try {
WebElement selected = driver.findElement(getXPathForLookupActiveCard());
WebElement selected = driver.findElement(getCssSelectorForLookupActiveCard());
return selected.getText();
} catch (NoSuchElementException nse) {
// No selected element found
@ -114,20 +114,20 @@ public class LoginTotpPage extends LanguageComboboxAwarePage {
}
private By getXPathForLookupAllCards() {
return By.xpath("//div[contains(@class, 'pf-c-tile otp-tile')]");
return By.xpath("//span[contains(@class, 'pf-c-tile__title')]");
}
private By getXPathForLookupActiveCard() {
return By.xpath("//div[contains(@class, 'otp-tile pf-m-selected')]");
private By getCssSelectorForLookupActiveCard() {
return By.cssSelector(".pf-c-tile__input:checked + .pf-c-tile .pf-c-tile__title");
}
private By getXPathForLookupCardWithName(String credentialName) {
return By.xpath("//div[contains(@class, 'otp-tile')][normalize-space() = '"+ credentialName +"']");
return By.xpath("//label[contains(@class, 'pf-c-tile')][normalize-space() = '"+ credentialName +"']");
}
public void selectOtpCredential(String credentialName) {
waitForElement(getXPathForLookupActiveCard());
waitForElement(getCssSelectorForLookupActiveCard());
WebElement webElement = driver.findElement(
getXPathForLookupCardWithName(credentialName));

View file

@ -1,77 +1,58 @@
<#import "template.ftl" as layout>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
<#if section="header">
${msg("doLogIn")}
<#elseif section="form">
<form id="kc-otp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
method="post">
<#if otpLogin.userOtpCredentials?size gt 1>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcInputWrapperClass!}">
<#list otpLogin.userOtpCredentials as otpCredential>
<div class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
<input type="hidden" value="${otpCredential.id}">
<div class="${properties.kcLoginOTPListItemHeaderClass!}">
<div class="${properties.kcLoginOTPListItemIconBodyClass!}">
<i class="${properties.kcLoginOTPListItemIconClass!}" aria-hidden="true"></i>
</div>
<div class="${properties.kcLoginOTPListItemTitleClass!}">${otpCredential.userLabel}</div>
</div>
</div>
</#list>
</div>
</div>
</#if>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="otp" class="${properties.kcLabelClass!}">${msg("loginOtpOneTime")}</label>
</div>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError('totp'); section>
<#if section="header">
${msg("doLogIn")}
<#elseif section="form">
<form id="kc-otp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
method="post">
<#if otpLogin.userOtpCredentials?size gt 1>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcInputWrapperClass!}">
<input id="otp" name="otp" autocomplete="off" type="text" class="${properties.kcInputClass!}"
autofocus aria-invalid="<#if messagesPerField.existsError('totp')>true</#if>"/>
<#list otpLogin.userOtpCredentials as otpCredential>
<input id="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListInputClass!}" type="radio" name="selectedCredentialId" value="${otpCredential.id}" <#if otpCredential.id == otpLogin.selectedCredentialId>checked="checked"</#if>>
<label for="kc-otp-credential-${otpCredential?index}" class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
<span class="${properties.kcLoginOTPListItemHeaderClass!}">
<span class="${properties.kcLoginOTPListItemIconBodyClass!}">
<i class="${properties.kcLoginOTPListItemIconClass!}" aria-hidden="true"></i>
</span>
<span class="${properties.kcLoginOTPListItemTitleClass!}">${otpCredential.userLabel}</span>
</span>
</label>
</#list>
</div>
</div>
</#if>
<#if messagesPerField.existsError('totp')>
<span id="input-error-otp-code" class="${properties.kcInputErrorMessageClass!}"
aria-live="polite">
${kcSanitize(messagesPerField.get('totp'))?no_esc}
</span>
</#if>
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="otp" class="${properties.kcLabelClass!}">${msg("loginOtpOneTime")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input id="otp" name="otp" autocomplete="off" type="text" class="${properties.kcInputClass!}"
autofocus aria-invalid="<#if messagesPerField.existsError('totp')>true</#if>"/>
<#if messagesPerField.existsError('totp')>
<span id="input-error-otp-code" class="${properties.kcInputErrorMessageClass!}"
aria-live="polite">
${kcSanitize(messagesPerField.get('totp'))?no_esc}
</span>
</#if>
</div>
</div>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>
<div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}">
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<input
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}" />
</div>
</div>
</form>
<script type="text/javascript" src="${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
// Card Single Select
$('.otp-tile').click(function() {
if ($(this).hasClass('pf-m-selected'))
{ $(this).removeClass('pf-m-selected'); $(this).children().removeAttr('name'); }
else
{ $('.otp-tile').removeClass('pf-m-selected');
$('.otp-tile').children().removeAttr('name');
$(this).addClass('pf-m-selected'); $(this).children().attr('name', 'selectedCredentialId'); }
});
var defaultCred = $('.otp-tile')[0];
if (defaultCred) {
defaultCred.click();
}
});
</script>
</#if>
</@layout.registrationLayout>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
<input
class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
name="login" id="kc-login" type="submit" value="${msg("doLogIn")}" />
</div>
</div>
</form>
</#if>
</@layout.registrationLayout>

View file

@ -35,11 +35,11 @@
.pf-c-tile:hover .pf-c-tile__icon {
color: #06c;
}
.pf-c-tile.pf-m-selected .pf-c-tile__title,
.pf-c-tile.pf-m-selected .pf-c-tile__icon {
.pf-c-tile__input:checked + .pf-c-tile .pf-c-tile__title,
.pf-c-tile__input:checked + .pf-c-tile .pf-c-tile__icon {
color: #06c;
}
.pf-c-tile.pf-m-selected {
.pf-c-tile__input:checked + .pf-c-tile {
border: 2px solid #06c;
border-radius: 3px;
}
@ -119,6 +119,7 @@
cursor: pointer;
background-color: var(--pf-c-tile--BackgroundColor);
grid-template-rows: min-content;
overflow: hidden;
}
.pf-c-tile::before {
@ -139,7 +140,7 @@
--pf-c-tile--before--BorderColor: var(--pf-c-tile--hover--before--BorderColor);
}
.pf-c-tile.pf-m-selected {
.pf-c-tile__input:checked + .pf-c-tile {
--pf-c-tile__title--Color: var(--pf-c-tile--m-selected__title--Color);
--pf-c-tile__icon--Color: var(--pf-c-tile--m-selected__icon--Color);
--pf-c-tile--before--BorderWidth: var(--pf-c-tile--m-selected--before--BorderWidth);
@ -166,6 +167,10 @@
--pf-c-tile__icon--FontSize: var(--pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize);
}
.pf-c-tile__input {
display: none;
}
.pf-c-tile__header {
display: flex;
align-items: center;

View file

@ -103,9 +103,10 @@ kcAuthenticatorWebAuthnClass=fa fa-key list-view-pf-icon-lg
kcAuthenticatorWebAuthnPasswordlessClass=fa fa-key list-view-pf-icon-lg
##### css classes for the OTP Login Form
kcLoginOTPListClass=pf-c-tile otp-tile
kcLoginOTPListClass=pf-c-tile
kcLoginOTPListInputClass=pf-c-tile__input
kcLoginOTPListItemHeaderClass=pf-c-tile__header
kcLoginOTPListItemIconBodyClass=pf-c-tile__icon otp-tile-icon
kcLoginOTPListItemIconBodyClass=pf-c-tile__icon
kcLoginOTPListItemIconClass=fa fa-mobile
kcLoginOTPListItemTitleClass=pf-c-tile__title