KEYCLOAK-14138 Upgrade OTP login screen

* edited related css and ftl theme resources
* added tile component
* fixed IE11 compatibility
* fixed affected tests

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
This commit is contained in:
Peter Zaoral 2020-10-20 01:44:01 +02:00 committed by Marek Posolda
parent a51e0cc484
commit c8a2f82a50
5 changed files with 225 additions and 26 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("card-pf-view-single-select"));
driver.findElement(By.className("otp-tile"));
Assert.assertTrue(expectedAvailability);
} catch (NoSuchElementException nse) {
Assert.assertFalse(expectedAvailability);
@ -114,15 +114,15 @@ public class LoginTotpPage extends LanguageComboboxAwarePage {
}
private By getXPathForLookupAllCards() {
return By.xpath("//div[contains(@class, 'card-pf-view-single-select')]//h2");
return By.xpath("//div[contains(@class, 'pf-c-tile otp-tile')]");
}
private By getXPathForLookupActiveCard() {
return By.xpath("//div[contains(@class, 'card-pf-view-single-select active')]//h2");
return By.xpath("//div[contains(@class, 'otp-tile pf-m-selected')]");
}
private By getXPathForLookupCardWithName(String credentialName) {
return By.xpath("//div[contains(@class, 'card-pf-view-single-select')]//h2[normalize-space() = '"+ credentialName +"']");
return By.xpath("//div[contains(@class, 'otp-tile')][normalize-space() = '"+ credentialName +"']");
}

View file

@ -9,13 +9,13 @@
<div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcInputWrapperClass!}">
<#list otpLogin.userOtpCredentials as otpCredential>
<div class="${properties.kcSelectOTPListClass!}">
<div class="${properties.kcLoginOTPListClass!}" tabindex="${otpCredential?index}">
<input type="hidden" value="${otpCredential.id}">
<div class="${properties.kcSelectOTPListItemClass!}">
<span class="${properties.kcAuthenticatorOtpCircleClass!}"></span>
<h2 class="${properties.kcSelectOTPItemHeadingClass!}">
${otpCredential.userLabel}
</h2>
<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>
@ -58,16 +58,16 @@
<script type="text/javascript">
$(document).ready(function() {
// Card Single Select
$('.card-pf-view-single-select').click(function() {
if ($(this).hasClass('active'))
{ $(this).removeClass('active'); $(this).children().removeAttr('name'); }
$('.otp-tile').click(function() {
if ($(this).hasClass('pf-m-selected'))
{ $(this).removeClass('pf-m-selected'); $(this).children().removeAttr('name'); }
else
{ $('.card-pf-view-single-select').removeClass('active');
$('.card-pf-view-single-select').children().removeAttr('name');
$(this).addClass('active'); $(this).children().attr('name', 'selectedCredentialId'); }
{ $('.otp-tile').removeClass('pf-m-selected');
$('.otp-tile').children().removeAttr('name');
$(this).addClass('pf-m-selected'); $(this).children().attr('name', 'selectedCredentialId'); }
});
var defaultCred = $('.card-pf-view-single-select')[0];
var defaultCred = $('.otp-tile')[0];
if (defaultCred) {
defaultCred.click();
}

View file

@ -696,10 +696,6 @@ ul#kc-totp-supported-apps {
width: 100%;
}
.login-pf-page .card-pf{
margin-bottom: 10px;
}
#kc-form-login div.form-group:last-of-type,
#kc-register-form div.form-group:last-of-type,
#kc-update-profile-form div.form-group:last-of-type {

View file

@ -0,0 +1,202 @@
/*Internet Explorer 11 compatibility workaround - IE does not support CSS variables */
@media all and (-ms-high-contrast: none),
(-ms-high-contrast: active) {
.pf-c-tile {
position: relative;
display: -ms-inline-grid;
display: inline-grid;
padding: 1.5rem 1.5rem 1.5rem 1.5rem;
margin-bottom: 0.25rem;
text-align: center;
cursor: pointer;
background-color: #fff;
grid-template-rows: -webkit-min-content;
-ms-grid-rows: -webkit-min-content;
-ms-grid-rows: min-content;
grid-template-rows: min-content;
}
.pf-c-tile::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
content: "";
border: 1px solid #d2d2d2;
border-radius: 3px;
}
.pf-c-tile:hover {
border: 1px solid #06c;
border-radius: 3px;
}
.pf-c-tile:hover .pf-c-tile__title,
.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 {
color: #06c;
}
.pf-c-tile.pf-m-selected {
border: 2px solid #06c;
border-radius: 3px;
}
.pf-c-tile.pf-m-disabled {
pointer-events: none;
}
.pf-c-tile__header {
display: flex;
align-items: center;
justify-content: center;
}
.pf-c-tile__header.pf-m-stacked {
flex-direction: column;
justify-content: initial;
}
.pf-c-tile__header.pf-m-stacked .pf-c-tile__icon {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0.25rem;
}
.pf-c-tile__title {
color: #6a6e73;
}
.pf-c-tile__body {
font-size: 0.75rem;
color: #6a6e73;
}
.pf-c-tile__icon {
margin-right: 0.5rem;
font-size: 1.5rem;
color: #6a6e73;
}
}
/*End of the IE11 workaround*/
.pf-c-tile {
--pf-c-tile--PaddingTop: var(--pf-global--spacer--lg);
--pf-c-tile--PaddingRight: var(--pf-global--spacer--lg);
--pf-c-tile--PaddingBottom: var(--pf-global--spacer--lg);
--pf-c-tile--PaddingLeft: var(--pf-global--spacer--lg);
--pf-c-tile--BackgroundColor: var(--pf-global--BackgroundColor--100);
--pf-c-tile--before--BorderColor: var(--pf-global--BorderColor--100);
--pf-c-tile--before--BorderWidth: var(--pf-global--BorderWidth--sm);
--pf-c-tile--before--BorderRadius: var(--pf-global--BorderRadius--sm);
--pf-c-tile--hover--before--BorderColor: var(--pf-global--primary-color--100);
--pf-c-tile--m-selected--before--BorderWidth: var(--pf-global--BorderWidth--md);
--pf-c-tile--m-selected--before--BorderColor: var(--pf-global--primary-color--100);
--pf-c-tile--focus--before--BorderWidth: var(--pf-global--BorderWidth--md);
--pf-c-tile--focus--before--BorderColor: var(--pf-global--primary-color--100);
--pf-c-tile--m-disabled--BackgroundColor: var(--pf-global--disabled-color--300);
--pf-c-tile__title--Color: var(--pf-global--Color--100);
--pf-c-tile--hover__title--Color: var(--pf-global--primary-color--100);
--pf-c-tile--m-selected__title--Color: var(--pf-global--primary-color--100);
--pf-c-tile--focus__title--Color: var(--pf-global--primary-color--100);
--pf-c-tile--m-disabled__title--Color: var(--pf-global--disabled-color--100);
--pf-c-tile__icon--MarginRight: var(--pf-global--spacer--sm);
--pf-c-tile__icon--FontSize: var(--pf-global--icon--FontSize--md);
--pf-c-tile__icon--Color: var(--pf-global--Color--100);
--pf-c-tile--hover__icon--Color: var(--pf-global--primary-color--100);
--pf-c-tile--m-selected__icon--Color: var(--pf-global--primary-color--100);
--pf-c-tile--m-disabled__icon--Color: var(--pf-global--disabled-color--100);
--pf-c-tile--focus__icon--Color: var(--pf-global--primary-color--100);
--pf-c-tile__header--m-stacked__icon--MarginBottom: var(--pf-global--spacer--xs);
--pf-c-tile__header--m-stacked__icon--FontSize: var(--pf-global--icon--FontSize--lg);
--pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize: var(--pf-global--icon--FontSize--xl);
--pf-c-tile__body--Color: var(--pf-global--Color--100);
--pf-c-tile__body--FontSize: var(--pf-global--FontSize--xs);
--pf-c-tile--m-disabled__body--Color: var(--pf-global--disabled-color--100);
position: relative;
display: inline-grid;
padding: var(--pf-c-tile--PaddingTop) var(--pf-c-tile--PaddingRight) var(--pf-c-tile--PaddingBottom) var(--pf-c-tile--PaddingLeft);
margin-bottom: 0.25rem;
text-align: center;
cursor: pointer;
background-color: var(--pf-c-tile--BackgroundColor);
grid-template-rows: min-content;
}
.pf-c-tile::before {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
content: "";
border: var(--pf-c-tile--before--BorderWidth) solid var(--pf-c-tile--before--BorderColor);
border-radius: var(--pf-c-tile--before--BorderRadius);
}
.pf-c-tile:hover {
--pf-c-tile__title--Color: var(--pf-c-tile--hover__title--Color);
--pf-c-tile__icon--Color: var(--pf-c-tile--hover__icon--Color);
--pf-c-tile--before--BorderColor: var(--pf-c-tile--hover--before--BorderColor);
}
.pf-c-tile.pf-m-selected {
--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);
--pf-c-tile--before--BorderColor: var(--pf-c-tile--m-selected--before--BorderColor);
}
.pf-c-tile:focus {
--pf-c-tile__title--Color: var(--pf-c-tile--focus__title--Color);
--pf-c-tile__icon--Color: var(--pf-c-tile--focus__icon--Color);
--pf-c-tile--before--BorderWidth: var(--pf-c-tile--focus--before--BorderWidth);
--pf-c-tile--before--BorderColor: var(--pf-c-tile--focus--before--BorderColor);
}
.pf-c-tile.pf-m-disabled {
--pf-c-tile--BackgroundColor: var(--pf-c-tile--m-disabled--BackgroundColor);
--pf-c-tile__title--Color: var(--pf-c-tile--m-disabled__title--Color);
--pf-c-tile__body--Color: var(--pf-c-tile--m-disabled__body--Color);
--pf-c-tile--before--BorderWidth: 0;
--pf-c-tile__icon--Color: var(--pf-c-tile--m-disabled__icon--Color);
pointer-events: none;
}
.pf-c-tile.pf-m-display-lg .pf-c-tile__header.pf-m-stacked {
--pf-c-tile__icon--FontSize: var(--pf-c-tile--m-display-lg__header--m-stacked__icon--FontSize);
}
.pf-c-tile__header {
display: flex;
align-items: center;
justify-content: center;
}
.pf-c-tile__header.pf-m-stacked {
--pf-c-tile__icon--MarginRight: 0;
--pf-c-tile__icon--FontSize: var(--pf-c-tile__header--m-stacked__icon--FontSize);
flex-direction: column;
justify-content: initial;
}
.pf-c-tile__header.pf-m-stacked .pf-c-tile__icon {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--pf-c-tile__header--m-stacked__icon--MarginBottom);
}
.pf-c-tile__title {
color: var(--pf-c-tile__title--Color);
}
.pf-c-tile__body {
font-size: var(--pf-c-tile__body--FontSize);
color: var(--pf-c-tile__body--Color);
}
.pf-c-tile__icon {
margin-right: var(--pf-c-tile__icon--MarginRight);
font-size: var(--pf-c-tile__icon--FontSize);
color: var(--pf-c-tile__icon--Color);
}

View file

@ -1,7 +1,7 @@
parent=base
import=common/keycloak
styles=css/login.css
styles=css/login.css css/tile.css
stylesCommon=web_modules/@fortawesome/fontawesome-free/css/icons/all.css web_modules/@patternfly/react-core/dist/styles/base.css web_modules/@patternfly/react-core/dist/styles/app.css node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/pficon/pficon.css
meta=viewport==width=device-width,initial-scale=1
@ -98,10 +98,11 @@ 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
kcSelectOTPListClass=card-pf card-pf-view card-pf-view-select card-pf-view-single-select
kcSelectOTPListItemClass=card-pf-body card-pf-top-element
kcAuthenticatorOtpCircleClass=fa fa-mobile card-pf-icon-circle
kcSelectOTPItemHeadingClass=card-pf-title text-center
kcLoginOTPListClass=pf-c-tile otp-tile
kcLoginOTPListItemHeaderClass=pf-c-tile__header
kcLoginOTPListItemIconBodyClass=pf-c-tile__icon otp-tile-icon
kcLoginOTPListItemIconClass=fa fa-mobile
kcLoginOTPListItemTitleClass=pf-c-tile__title
##### css classes for identity providers logos
kcCommonLogoIdP=kc-social-provider-logo kc-social-gray