Skip to content

Commit

Permalink
KEYCLOAK-14138 Upgrade OTP login screen
Browse files Browse the repository at this point in the history
* edited related css and ftl theme resources
* added tile component
* fixed IE11 compatibility
* fixed affected tests

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
  • Loading branch information
Pepo48 authored and mposolda committed Dec 3, 2020
1 parent a51e0cc commit c8a2f82
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public void open() {
// 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);
Expand All @@ -114,15 +114,15 @@ public String getSelectedOtpCredential() {
}

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 +"']");
}


Expand Down
26 changes: 13 additions & 13 deletions themes/src/main/resources/theme/base/login/login-otp.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
202 changes: 202 additions & 0 deletions themes/src/main/resources/theme/keycloak/login/resources/css/tile.css
Original file line number Diff line number Diff line change
@@ -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);
}
11 changes: 6 additions & 5 deletions themes/src/main/resources/theme/keycloak/login/theme.properties
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit c8a2f82

Please sign in to comment.