[ACS-7385][ADF] Break Login dependency on Material Module (#9501)

This commit is contained in:
tamaragruszka
2024-04-09 18:13:19 +02:00
committed by GitHub
parent 5494aa8728
commit a5e7fcc10e
24 changed files with 382 additions and 400 deletions

View File

@@ -5,7 +5,11 @@
[showLoginActions]="false" [showLoginActions]="false"
[backgroundImageUrl]="''" [backgroundImageUrl]="''"
(success)="onLoginSuccess($event)"> (success)="onLoginSuccess($event)">
<adf-login-header><ng-template></ng-template></adf-login-header> <adf-login-header>
<adf-login-footer><ng-template></ng-template></adf-login-footer> <ng-template></ng-template>
</adf-login-header>
<adf-login-footer>
<ng-template></ng-template>
</adf-login-footer>
</adf-login> </adf-login>
</div> </div>

View File

@@ -15,13 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { BasicAlfrescoAuthService, CoreTestingModule, LoginDialogPanelComponent } from '@alfresco/adf-core';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginDialogPanelComponent } from './login-dialog-panel.component';
import { of } from 'rxjs';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { BasicAlfrescoAuthService } from '../../auth/basic-auth/basic-alfresco-auth.service'; import { of } from 'rxjs';
import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service'; import { OidcAuthenticationService } from '../../../auth/services/oidc-authentication.service';
describe('LoginDialogPanelComponent', () => { describe('LoginDialogPanelComponent', () => {
let component: LoginDialogPanelComponent; let component: LoginDialogPanelComponent;
@@ -38,7 +36,7 @@ describe('LoginDialogPanelComponent', () => {
CoreTestingModule CoreTestingModule
], ],
providers: [ providers: [
{ provide: OidcAuthenticationService, useValue: {}} { provide: OidcAuthenticationService, useValue: {} }
] ]
}); });
fixture = TestBed.createComponent(LoginDialogPanelComponent); fixture = TestBed.createComponent(LoginDialogPanelComponent);

View File

@@ -15,14 +15,22 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ViewEncapsulation, ViewChild, Output, EventEmitter } from '@angular/core'; import { Component, EventEmitter, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { LoginComponent } from './login.component'; import { LoginFooterDirective } from '../../directives/login-footer.directive';
import { LoginSuccessEvent } from '../models/login-success.event'; import { LoginHeaderDirective } from '../../directives/login-header.directive';
import { LoginSuccessEvent } from '../../models/login-success.event';
import { LoginComponent } from '../login/login.component';
@Component({ @Component({
selector: 'adf-login-dialog-panel', selector: 'adf-login-dialog-panel',
standalone: true,
templateUrl: './login-dialog-panel.component.html', templateUrl: './login-dialog-panel.component.html',
styleUrls: ['./login-dialog-panel.component.scss'], styleUrls: ['./login-dialog-panel.component.scss'],
imports: [
LoginComponent,
LoginHeaderDirective,
LoginFooterDirective
],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class LoginDialogPanelComponent { export class LoginDialogPanelComponent {

View File

@@ -1,27 +0,0 @@
<header
mat-dialog-title
data-automation-id="login-dialog-title">
{{data?.title}}
</header>
<mat-dialog-content class="adf-login-dialog-content">
<adf-login-dialog-panel #adfLoginPanel (success)="onLoginSuccess($event)">
</adf-login-dialog-panel>
</mat-dialog-content>
<mat-dialog-actions class="adf-login-dialog-content-actions" align="end">
<button
mat-button (click)="close()"
data-automation-id="login-dialog-actions-cancel">
{{ 'LOGIN.DIALOG.CANCEL' | translate }}
</button>
<button
mat-button
class="choose-action"
data-automation-id="login-dialog-actions-perform"
[disabled]="!isFormValid()"
(click)="submitForm()">
{{ buttonActionName | translate}}
</button>
</mat-dialog-actions>

View File

@@ -0,0 +1,24 @@
<header mat-dialog-title
data-automation-id="login-dialog-title">
{{ data?.title }}
</header>
<mat-dialog-content class="adf-login-dialog-content">
<adf-login-dialog-panel #adfLoginPanel (success)="onLoginSuccess($event)">
</adf-login-dialog-panel>
</mat-dialog-content>
<mat-dialog-actions class="adf-login-dialog-content-actions" align="end">
<button mat-button (click)="close()"
data-automation-id="login-dialog-actions-cancel">
{{ 'LOGIN.DIALOG.CANCEL' | translate }}
</button>
<button mat-button
class="choose-action"
data-automation-id="login-dialog-actions-perform"
[disabled]="!isFormValid()"
(click)="submitForm()">
{{ buttonActionName | translate }}
</button>
</mat-dialog-actions>

View File

@@ -15,14 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { Meta, moduleMetadata, Story } from '@storybook/angular';
import { CoreStoryModule } from '../../testing/core.story.module';
import { RouterTestingModule } from '@angular/router/testing';
import { LoginModule } from './../login.module';
import { LoginDialogStorybookComponent } from './login-dialog.stories.component';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { AuthenticationService } from '../../auth/services/authentication.service'; import { RouterTestingModule } from '@angular/router/testing';
import { AuthenticationMock } from '../../auth/mock/authentication.service.mock'; import { Meta, moduleMetadata, Story } from '@storybook/angular';
import { AuthenticationService } from '../../../auth';
import { AuthenticationMock } from '../../../auth/mock/authentication.service.mock';
import { CoreStoryModule } from '../../../testing/core.story.module';
import { LoginModule } from '../../login.module';
import { LoginDialogStorybookComponent } from './login-dialog.stories.component';
export default { export default {
component: LoginDialogStorybookComponent, component: LoginDialogStorybookComponent,

View File

@@ -15,14 +15,24 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, Inject, ViewEncapsulation, ViewChild } from '@angular/core'; import { Component, Inject, ViewChild, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
import { LoginDialogPanelComponent } from '../login-dialog-panel/login-dialog-panel.component';
import { LoginDialogComponentData } from './login-dialog-component-data.interface'; import { LoginDialogComponentData } from './login-dialog-component-data.interface';
import { LoginDialogPanelComponent } from './login-dialog-panel.component';
@Component({ @Component({
selector: 'adf-login-dialog', selector: 'adf-login-dialog',
standalone: true,
templateUrl: './login-dialog.component.html', templateUrl: './login-dialog.component.html',
styleUrls: ['./login-dialog.component.scss'], styleUrls: ['./login-dialog.component.scss'],
imports: [
MatDialogModule,
LoginDialogPanelComponent,
TranslateModule,
MatButtonModule
],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class LoginDialogComponent { export class LoginDialogComponent {

View File

@@ -16,6 +16,7 @@
*/ */
import { Component, Output, EventEmitter } from '@angular/core'; import { Component, Output, EventEmitter } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { LoginDialogComponent } from './login-dialog.component'; import { LoginDialogComponent } from './login-dialog.component';
@@ -23,9 +24,14 @@ import { LoginDialogComponentData } from './login-dialog-component-data.interfac
@Component({ @Component({
selector: 'adf-login-dialog-storybook', selector: 'adf-login-dialog-storybook',
template: `<button mat-raised-button (click)="openLoginDialog()"> standalone: true,
Open dialog imports: [
</button>` MatButtonModule
],
template: `
<button mat-raised-button (click)="openLoginDialog()">
Open dialog
</button>`
}) })
export class LoginDialogStorybookComponent { export class LoginDialogStorybookComponent {

View File

@@ -1,218 +0,0 @@
<div class="adf-login-content" [style.background-image]="getBackgroundUrlImageUrl()">
<div class="adf-ie11FixerParent">
<div class="adf-ie11FixerChild">
<mat-card class="adf-login-card-wide">
<form
id="adf-login-form"
[formGroup]="form"
autocomplete="off"
(submit)="onSubmit(form.value)">
<mat-card-header class="adf-login-card-header-text">
<mat-card-title>
<div class="adf-alfresco-logo">
<!--HEADER TEMPLATE-->
<ng-template
*ngIf="headerTemplate"
ngFor
[ngForOf]="[data]"
[ngForTemplate]="headerTemplate">
</ng-template>
<img
*ngIf="!headerTemplate"
id="adf-login-img-logo"
class="adf-img-logo"
[src]="logoImageUrl"
alt="{{'LOGIN.LOGO' | translate }}">
</div>
</mat-card-title>
</mat-card-header>
<mat-card-content class="adf-login-controls">
<!--ERRORS AREA-->
<div class="adf-error-container">
<div
*ngIf="isError"
id="login-error"
data-automation-id="login-error"
class="adf-error adf-error-message">
<mat-icon class="adf-error-icon">warning</mat-icon>
<span class="adf-login-error-message">
{{errorMsg | translate }}
</span>
</div>
</div>
<div *ngIf="!ssoLogin">
<!--USERNAME FIELD-->
<div
class="adf-login__field"
[ngClass]="{'adf-is-invalid': isErrorStyle(form.controls.username)}">
<mat-form-field
class="adf-full-width adf-login-form-field"
floatPlaceholder="never"
color="primary">
<input
matInput
type="text"
class="adf-full-width"
formControlName="username"
id="username"
data-automation-id="username"
placeholder="{{'LOGIN.LABEL.USERNAME' | translate }}"
autocapitalize="none"
(blur)="trimUsername($event)">
</mat-form-field>
<span
*ngIf="formError['username']"
class="adf-login-validation"
for="username">
<span
id="username-error"
class="adf-login-error"
data-automation-id="username-error">
{{formError['username'] | translate}}
</span>
</span>
</div>
<!--PASSWORD FIELD-->
<div class="adf-login__field">
<mat-form-field
class="adf-full-width adf-login-form-field"
floatPlaceholder="never"
color="primary">
<input
matInput
placeholder="{{'LOGIN.LABEL.PASSWORD' | translate }}"
[type]="isPasswordShow ? 'text' : 'password'"
formControlName="password"
id="password"
data-automation-id="password">
<button
matSuffix
mat-icon-button
type="button"
[attr.aria-label]="(isPasswordShow ? 'LOGIN.ARIA-LABEL.HIDE-PASSWORD' : 'LOGIN.ARIA-LABEL.SHOW-PASSWORD') | translate"
(click)="toggleShowPassword($event)"
(keyup.enter)="toggleShowPassword($event)"
[attr.data-automation-id]="isPasswordShow ? 'hide_password' : 'show_password'">
<mat-icon class="adf-login-form-password-icon adf-login-password-icon">
{{ isPasswordShow ? 'visibility' : 'visibility_off' }}
</mat-icon>
</button>
</mat-form-field>
<span
class="adf-login-validation"
for="password"
*ngIf="formError['password']">
<span
id="password-required"
class="adf-login-error"
data-automation-id="password-required">
{{formError['password'] | translate}}
</span>
</span>
</div>
<!--CUSTOM CONTENT-->
<ng-content></ng-content>
<br>
<button
type="submit"
id="login-button"
class="adf-login-button"
mat-raised-button
color="accent"
[class.adf-isChecking]="actualLoginStep === LoginSteps.Checking"
[class.adf-isWelcome]="actualLoginStep === LoginSteps.Welcome"
data-automation-id="login-button"
[disabled]="!form.valid"
[attr.aria-label]="'LOGIN.BUTTON.LOGIN' | translate">
<span
*ngIf="actualLoginStep === LoginSteps.Landing"
class="adf-login-button-label">
{{'LOGIN.BUTTON.LOGIN' | translate }}
</span>
<div
*ngIf="actualLoginStep === LoginSteps.Checking"
class="adf-interactive-login-label">
<span class="adf-login-button-label">
{{ 'LOGIN.BUTTON.CHECKING' | translate}}
</span>
<div class="adf-login-spinner-container">
<mat-spinner
id="checking-spinner"
class="adf-login-checking-spinner"
[strokeWidth]="4"
[diameter]="25">
</mat-spinner>
</div>
</div>
<div
*ngIf="actualLoginStep === LoginSteps.Welcome"
class="adf-interactive-login-label">
<span class="adf-login-button-label">{{ 'LOGIN.BUTTON.WELCOME' | translate }}</span>
<mat-icon class="adf-welcome-icon">done</mat-icon>
</div>
</button>
<div *ngIf="showRememberMe" class="adf-login__remember-me">
<mat-checkbox
id="adf-login-remember"
color="primary"
class="adf-login-remember-me"
[checked]="rememberMe"
(change)="rememberMe = !rememberMe">
<div class="adf-login-remember-me-label">
{{ 'LOGIN.LABEL.REMEMBER' | translate }}
</div>
</mat-checkbox>
</div>
</div>
<div *ngIf="ssoLogin">
<button
type="button"
(click)="implicitLogin()"
id="login-button-sso"
[attr.aria-label]="'LOGIN.BUTTON.SSO' | translate"
class="adf-login-button"
mat-raised-button color="primary"
data-automation-id="login-button-sso">
<span class="adf-login-button-label">{{ 'LOGIN.BUTTON.SSO' | translate }}</span>
</button>
</div>
</mat-card-content>
<mat-card-actions *ngIf="footerTemplate || showLoginActions">
<div class="adf-login-action-container">
<!--FOOTER TEMPLATE-->
<ng-template
*ngIf="footerTemplate"
ngFor
[ngForOf]="[data]"
[ngForTemplate]="footerTemplate">
</ng-template>
<div class="adf-login-action" *ngIf="!footerTemplate && showLoginActions">
<div id="adf-login-action-left" class="adf-login-action-left">
<a href="{{needHelpLink}}">{{'LOGIN.ACTION.HELP' | translate }}</a>
</div>
<div id="adf-login-action-right" class="adf-login-action-right">
<a href="{{registerLink}}">{{'LOGIN.ACTION.REGISTER' | translate }}</a>
</div>
</div>
</div>
</mat-card-actions>
</form>
</mat-card>
<div class="adf-copyright" data-automation-id="login-copyright">
{{ copyrightText }}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,200 @@
<div class="adf-login-content" [style.background-image]="'url(' + backgroundImageUrl + ')'">
<div class="adf-ie11FixerParent">
<div class="adf-ie11FixerChild">
<mat-card class="adf-login-card-wide">
<form id="adf-login-form"
[formGroup]="form"
autocomplete="off"
(submit)="onSubmit(form.value)">
<mat-card-header class="adf-login-card-header-text">
<mat-card-title>
<div class="adf-alfresco-logo">
<!--HEADER TEMPLATE-->
<ng-template *ngIf="headerTemplate"
ngFor
[ngForOf]="[data]"
[ngForTemplate]="headerTemplate">
</ng-template>
<img *ngIf="!headerTemplate"
id="adf-login-img-logo"
class="adf-img-logo"
[src]="logoImageUrl"
alt="{{ 'LOGIN.LOGO' | translate }}">
</div>
</mat-card-title>
</mat-card-header>
<mat-card-content class="adf-login-controls">
<!--ERRORS AREA-->
<div class="adf-error-container">
<div *ngIf="isError"
id="login-error"
data-automation-id="login-error"
class="adf-error adf-error-message">
<mat-icon class="adf-error-icon">warning</mat-icon>
<span class="adf-login-error-message">
{{ errorMsg | translate }}
</span>
</div>
</div>
<div *ngIf="!ssoLogin">
<!--USERNAME FIELD-->
<div class="adf-login__field"
[ngClass]="{'adf-is-invalid': isErrorStyle(form.controls.username)}">
<mat-form-field class="adf-full-width adf-login-form-field"
floatPlaceholder="never"
color="primary">
<input matInput
type="text"
class="adf-full-width"
formControlName="username"
id="username"
data-automation-id="username"
placeholder="{{'LOGIN.LABEL.USERNAME' | translate }}"
autocapitalize="none"
(blur)="trimUsername($event)">
</mat-form-field>
<span *ngIf="formError['username']"
class="adf-login-validation"
for="username">
<span id="username-error"
class="adf-login-error"
data-automation-id="username-error">
{{ formError['username'] | translate }}
</span>
</span>
</div>
<!--PASSWORD FIELD-->
<div class="adf-login__field">
<mat-form-field class="adf-full-width adf-login-form-field"
floatPlaceholder="never"
color="primary">
<input matInput
placeholder="{{'LOGIN.LABEL.PASSWORD' | translate }}"
[type]="isPasswordShow ? 'text' : 'password'"
formControlName="password"
id="password"
data-automation-id="password">
<button matSuffix
mat-icon-button
type="button"
[attr.aria-label]="(isPasswordShow ? 'LOGIN.ARIA-LABEL.HIDE-PASSWORD' : 'LOGIN.ARIA-LABEL.SHOW-PASSWORD') | translate"
(click)="toggleShowPassword($event)"
(keyup.enter)="toggleShowPassword($event)"
[attr.data-automation-id]="isPasswordShow ? 'hide_password' : 'show_password'">
<mat-icon class="adf-login-form-password-icon adf-login-password-icon">
{{ isPasswordShow ? 'visibility' : 'visibility_off' }}
</mat-icon>
</button>
</mat-form-field>
<span class="adf-login-validation"
for="password"
*ngIf="formError['password']">
<span id="password-required"
class="adf-login-error"
data-automation-id="password-required">
{{ formError['password'] | translate }}
</span>
</span>
</div>
<!--CUSTOM CONTENT-->
<ng-content></ng-content>
<br>
<button type="submit"
id="login-button"
class="adf-login-button"
mat-raised-button
color="accent"
[class.adf-isChecking]="actualLoginStep === LoginSteps.Checking"
[class.adf-isWelcome]="actualLoginStep === LoginSteps.Welcome"
data-automation-id="login-button"
[disabled]="!form.valid"
[attr.aria-label]="'LOGIN.BUTTON.LOGIN' | translate">
<span *ngIf="actualLoginStep === LoginSteps.Landing"
class="adf-login-button-label">
{{ 'LOGIN.BUTTON.LOGIN' | translate }}
</span>
<div *ngIf="actualLoginStep === LoginSteps.Checking"
class="adf-interactive-login-label">
<span class="adf-login-button-label">
{{ 'LOGIN.BUTTON.CHECKING' | translate }}
</span>
<div class="adf-login-spinner-container">
<mat-spinner id="checking-spinner"
class="adf-login-checking-spinner"
[strokeWidth]="4"
[diameter]="25">
</mat-spinner>
</div>
</div>
<div *ngIf="actualLoginStep === LoginSteps.Welcome"
class="adf-interactive-login-label">
<span class="adf-login-button-label">
{{ 'LOGIN.BUTTON.WELCOME' | translate }}
</span>
<mat-icon class="adf-welcome-icon">done</mat-icon>
</div>
</button>
<div *ngIf="showRememberMe" class="adf-login__remember-me">
<mat-checkbox id="adf-login-remember"
color="primary"
class="adf-login-remember-me"
[checked]="rememberMe"
(change)="rememberMe = !rememberMe">
<div class="adf-login-remember-me-label">
{{ 'LOGIN.LABEL.REMEMBER' | translate }}
</div>
</mat-checkbox>
</div>
</div>
<div *ngIf="ssoLogin">
<button type="button"
(click)="implicitLogin()"
id="login-button-sso"
[attr.aria-label]="'LOGIN.BUTTON.SSO' | translate"
class="adf-login-button"
mat-raised-button color="primary"
data-automation-id="login-button-sso">
<span class="adf-login-button-label">
{{ 'LOGIN.BUTTON.SSO' | translate }}
</span>
</button>
</div>
</mat-card-content>
<mat-card-actions *ngIf="footerTemplate || showLoginActions">
<div class="adf-login-action-container">
<!--FOOTER TEMPLATE-->
<ng-template *ngIf="footerTemplate"
ngFor
[ngForOf]="[data]"
[ngForTemplate]="footerTemplate">
</ng-template>
<div class="adf-login-action" *ngIf="!footerTemplate && showLoginActions">
<div id="adf-login-action-left" class="adf-login-action-left">
<a href="{{ needHelpLink }}">{{ 'LOGIN.ACTION.HELP' | translate }}</a>
</div>
<div id="adf-login-action-right" class="adf-login-action-right">
<a href="{{ registerLink }}">{{ 'LOGIN.ACTION.REGISTER' | translate }}</a>
</div>
</div>
</div>
</mat-card-actions>
</form>
</mat-card>
<div class="adf-copyright" data-automation-id="login-copyright">
{{ copyrightText }}
</div>
</div>
</div>
</div>

View File

@@ -1,4 +1,4 @@
@import '../../styles/mixins'; @import '../../../styles/mixins';
.adf-login { .adf-login {
@include flex-column; @include flex-column;
@@ -63,7 +63,7 @@
box-sizing: border-box; box-sizing: border-box;
} }
@media (max-width: 482px) { @media screen and (width <= 482px) {
.adf-login-card-wide { .adf-login-card-wide {
width: calc(100% - 32px); width: calc(100% - 32px);
} }
@@ -185,6 +185,8 @@
.adf-login__field { .adf-login__field {
margin: 1em 0 0; margin: 1em 0 0;
display: block;
padding-bottom: 18px;
font-size: var(--theme-subheading-2-font-size); font-size: var(--theme-subheading-2-font-size);
& input:-webkit-autofill { & input:-webkit-autofill {
@@ -221,13 +223,6 @@
opacity: 0.87; opacity: 0.87;
} }
.adf-login__field {
display: block;
margin-left: auto;
margin-right: auto;
padding-bottom: 18px;
}
.adf-login-remember-me:has(.adf-login-remember-me-label) { .adf-login-remember-me:has(.adf-login-remember-me-label) {
color: var(--adf-theme-foreground-text-color); color: var(--adf-theme-foreground-text-color);
} }

View File

@@ -15,21 +15,23 @@
* limitations under the License. * limitations under the License.
*/ */
import {
AppConfigService,
AuthenticationService,
BasicAlfrescoAuthService,
CoreTestingModule,
LoginErrorEvent,
LoginSuccessEvent,
LogService,
UserPreferencesService
} from '@alfresco/adf-core';
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { Validators } from '@angular/forms'; import { Validators } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { UserPreferencesService } from '../../common/services/user-preferences.service';
import { AppConfigService } from '../../app-config/app-config.service';
import { AuthenticationService } from '../../auth/services/authentication.service';
import { LoginErrorEvent } from '../models/login-error.event';
import { LoginSuccessEvent } from '../models/login-success.event';
import { LoginComponent } from './login.component';
import { EMPTY, of, throwError } from 'rxjs'; import { EMPTY, of, throwError } from 'rxjs';
import { CoreTestingModule } from '../../testing/core.testing.module'; import { OidcAuthenticationService } from '../../../auth/services/oidc-authentication.service';
import { LogService } from '../../common/services/log.service'; import { LoginComponent } from './login.component';
import { BasicAlfrescoAuthService } from '../../auth/basic-auth/basic-alfresco-auth.service';
import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';
describe('LoginComponent', () => { describe('LoginComponent', () => {
let component: LoginComponent; let component: LoginComponent;
@@ -65,7 +67,8 @@ describe('LoginComponent', () => {
providers: [ providers: [
{ {
provide: OidcAuthenticationService, useValue: { provide: OidcAuthenticationService, useValue: {
ssoLogin: () => { }, ssoLogin: () => {
},
isPublicUrl: () => false, isPublicUrl: () => false,
hasValidIdToken: () => false, hasValidIdToken: () => false,
isLoggedIn: () => false isLoggedIn: () => false
@@ -581,7 +584,7 @@ describe('LoginComponent', () => {
loginWithCredentials('fake-username-ECM-access-error', 'fake-password'); loginWithCredentials('fake-username-ECM-access-error', 'fake-password');
})); }));
}); });
it('should trim the username value', () => { it('should trim the username value', () => {
usernameInput.value = 'username '; usernameInput.value = 'username ';
@@ -623,7 +626,7 @@ describe('LoginComponent', () => {
}); });
loginWithCredentials('fake-username', 'fake-password'); loginWithCredentials('fake-username', 'fake-password');
}); });
it('should emit success event after the login has succeeded and discard password', fakeAsync(() => { it('should emit success event after the login has succeeded and discard password', fakeAsync(() => {
spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' })); spyOn(basicAlfrescoAuthService, 'login').and.returnValue(of({ type: 'type', ticket: 'ticket' }));

View File

@@ -15,13 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { Meta, moduleMetadata, Story } from '@storybook/angular';
import { CoreStoryModule } from '../../testing/core.story.module';
import { LoginModule } from '../login.module';
import { LoginComponent } from './login.component';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { AuthenticationService } from './../../auth/services/authentication.service'; import { Meta, moduleMetadata, Story } from '@storybook/angular';
import { AuthenticationMock } from '../../auth/mock/authentication.service.mock'; import { AuthenticationService } from '../../../auth';
import { AuthenticationMock } from '../../../auth/mock/authentication.service.mock';
import { CoreStoryModule } from '../../../testing/core.story.module';
import { LoginModule } from '../../login.module';
import { LoginComponent } from './login.component';
export default { export default {
component: LoginComponent, component: LoginComponent,

View File

@@ -15,22 +15,29 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewEncapsulation, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common';
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router'; import { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { AuthenticationService } from '../../auth/services/authentication.service'; import { MatButtonModule } from '@angular/material/button';
import { TranslationService } from '../../translation/translation.service'; import { MatCardModule } from '@angular/material/card';
import { UserPreferencesService } from '../../common/services/user-preferences.service'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { LoginErrorEvent } from '../models/login-error.event'; import { MatIconModule } from '@angular/material/icon';
import { LoginSubmitEvent } from '../models/login-submit.event'; import { MatInputModule } from '@angular/material/input';
import { LoginSuccessEvent } from '../models/login-success.event'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser'; import { TranslateModule } from '@ngx-translate/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { BasicAlfrescoAuthService } from '../../auth/basic-auth/basic-alfresco-auth.service'; import { AppConfigService, AppConfigValues } from '../../../app-config';
import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service'; import { AuthenticationService, BasicAlfrescoAuthService } from '../../../auth';
import { OidcAuthenticationService } from '../../../auth/services/oidc-authentication.service';
import { UserPreferencesService } from '../../../common';
import { TranslationService } from '../../../translation';
import { LoginErrorEvent } from '../../models/login-error.event';
import { LoginSubmitEvent } from '../../models/login-submit.event';
import { LoginSuccessEvent } from '../../models/login-success.event';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
enum LoginSteps { enum LoginSteps {
@@ -47,14 +54,27 @@ interface ValidationMessage {
interface LoginFormValues { interface LoginFormValues {
username: string; username: string;
password: string; password: string;
}; }
@Component({ @Component({
selector: 'adf-login', selector: 'adf-login',
standalone: true,
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'], styleUrls: ['./login.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
host: {class: 'adf-login'} imports: [
CommonModule,
MatCardModule,
ReactiveFormsModule,
TranslateModule,
MatIconModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatProgressSpinnerModule,
MatCheckboxModule
],
host: { class: 'adf-login' }
}) })
export class LoginComponent implements OnInit, OnDestroy { export class LoginComponent implements OnInit, OnDestroy {
isPasswordShow: boolean = false; isPasswordShow: boolean = false;
@@ -137,8 +157,7 @@ export class LoginComponent implements OnInit, OnDestroy {
private router: Router, private router: Router,
private appConfig: AppConfigService, private appConfig: AppConfigService,
private userPreferences: UserPreferencesService, private userPreferences: UserPreferencesService,
private route: ActivatedRoute, private route: ActivatedRoute
private sanitizer: DomSanitizer
) {} ) {}
ngOnInit() { ngOnInit() {
@@ -164,7 +183,7 @@ export class LoginComponent implements OnInit, OnDestroy {
const url = params['redirectUrl']; const url = params['redirectUrl'];
const provider = this.appConfig.get<string>(AppConfigValues.PROVIDERS); const provider = this.appConfig.get<string>(AppConfigValues.PROVIDERS);
this.basicAlfrescoAuthService.setRedirect({provider, url}); this.basicAlfrescoAuthService.setRedirect({ provider, url });
}); });
} }
@@ -197,7 +216,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.disableError(); this.disableError();
const args = new LoginSubmitEvent({ const args = new LoginSubmitEvent({
controls: {username: this.form.controls.username} controls: { username: this.form.controls.username }
}); });
this.executeSubmit.emit(args); this.executeSubmit.emit(args);
@@ -243,30 +262,29 @@ export class LoginComponent implements OnInit, OnDestroy {
} }
performLogin(values: { username: string; password: string }) { performLogin(values: { username: string; password: string }) {
this.authService.login(values.username, values.password, this.rememberMe) this.authService.login(values.username, values.password, this.rememberMe).subscribe(
.subscribe( async (token: any) => {
async (token: any) => { const redirectUrl = this.basicAlfrescoAuthService.getRedirect();
const redirectUrl = this.basicAlfrescoAuthService.getRedirect();
this.actualLoginStep = LoginSteps.Welcome; this.actualLoginStep = LoginSteps.Welcome;
this.userPreferences.setStoragePrefix(values.username); this.userPreferences.setStoragePrefix(values.username);
values.password = null; values.password = null;
this.success.emit(new LoginSuccessEvent(token, values.username, null)); this.success.emit(new LoginSuccessEvent(token, values.username, null));
if (redirectUrl) { if (redirectUrl) {
this.basicAlfrescoAuthService.setRedirect(null); this.basicAlfrescoAuthService.setRedirect(null);
await this.router.navigateByUrl(redirectUrl); await this.router.navigateByUrl(redirectUrl);
} else if (this.successRoute) { } else if (this.successRoute) {
await this.router.navigate([this.successRoute]); await this.router.navigate([this.successRoute]);
}
},
(err: any) => {
this.actualLoginStep = LoginSteps.Landing;
this.displayErrorMessage(err);
this.isError = true;
this.error.emit(new LoginErrorEvent(err));
} }
); },
(err: any) => {
this.actualLoginStep = LoginSteps.Landing;
this.displayErrorMessage(err);
this.isError = true;
this.error.emit(new LoginErrorEvent(err));
}
);
} }
/** /**
@@ -342,10 +360,6 @@ export class LoginComponent implements OnInit, OnDestroy {
event.target.value = event.target.value.trim(); event.target.value = event.target.value.trim();
} }
getBackgroundUrlImageUrl(): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`url(${this.backgroundImageUrl})`);
}
/** /**
* Default formError values * Default formError values
*/ */

View File

@@ -15,10 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { TestBed, ComponentFixture } from '@angular/core/testing'; import { CoreTestingModule, LoginComponent, LoginFooterDirective } from '@alfresco/adf-core';
import { LoginComponent } from '../components/login.component'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginFooterDirective } from './login-footer.directive';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service'; import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';

View File

@@ -15,27 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core';
AfterContentInit, import { LoginComponent } from '../components/login/login.component';
ContentChild,
Directive,
TemplateRef
} from '@angular/core';
import { LoginComponent } from '../components/login.component';
/**
* Directive selectors without adf- prefix will be deprecated on 3.0.0
*/
@Directive({ @Directive({
selector: 'adf-login-footer, login-footer' selector: 'adf-login-footer',
standalone: true
}) })
export class LoginFooterDirective implements AfterContentInit { export class LoginFooterDirective implements AfterContentInit {
@ContentChild(TemplateRef) @ContentChild(TemplateRef)
template: any; template: any;
constructor( constructor(private alfrescoLoginComponent: LoginComponent) {
private alfrescoLoginComponent: LoginComponent) {
} }
ngAfterContentInit() { ngAfterContentInit() {

View File

@@ -15,10 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { TestBed, ComponentFixture } from '@angular/core/testing'; import { CoreTestingModule, LoginComponent, LoginHeaderDirective } from '@alfresco/adf-core';
import { LoginComponent } from '../components/login.component'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginHeaderDirective } from './login-header.directive';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service'; import { OidcAuthenticationService } from '../../auth/services/oidc-authentication.service';

View File

@@ -15,27 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { AfterContentInit, ContentChild, Directive, TemplateRef } from '@angular/core';
AfterContentInit, import { LoginComponent } from '../components/login/login.component';
ContentChild,
Directive,
TemplateRef
} from '@angular/core';
import { LoginComponent } from '../components/login.component';
/**
* Directive selectors without adf- prefix will be deprecated on 3.0.0
*/
@Directive({ @Directive({
selector: 'adf-login-header, login-header' selector: 'adf-login-header',
standalone: true
}) })
export class LoginHeaderDirective implements AfterContentInit { export class LoginHeaderDirective implements AfterContentInit {
@ContentChild(TemplateRef) @ContentChild(TemplateRef)
template: any; template: any;
constructor( constructor(private alfrescoLoginComponent: LoginComponent) {
private alfrescoLoginComponent: LoginComponent) {
} }
ngAfterContentInit() { ngAfterContentInit() {

View File

@@ -15,29 +15,16 @@
* limitations under the License. * limitations under the License.
*/ */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { LoginDialogPanelComponent } from './components/login-dialog-panel/login-dialog-panel.component';
import { RouterModule } from '@angular/router'; import { LoginDialogComponent } from './components/login-dialog/login-dialog.component';
import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../material.module';
import { LoginComponent } from './components/login.component'; import { LoginComponent } from './components/login/login.component';
import { LoginFooterDirective } from './directives/login-footer.directive'; import { LoginFooterDirective } from './directives/login-footer.directive';
import { LoginHeaderDirective } from './directives/login-header.directive'; import { LoginHeaderDirective } from './directives/login-header.directive';
import { LoginDialogComponent } from './components/login-dialog.component';
import { LoginDialogPanelComponent } from './components/login-dialog-panel.component';
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
TranslateModule
],
declarations: [
LoginComponent, LoginComponent,
LoginFooterDirective, LoginFooterDirective,
LoginHeaderDirective, LoginHeaderDirective,

View File

@@ -19,6 +19,6 @@ export class LoginSuccessEvent {
constructor( constructor(
public token: any, public token: any,
public username: string, public username: string,
public password: string) { public password: string
} ) {}
} }

View File

@@ -18,10 +18,10 @@
export * from './directives/login-header.directive'; export * from './directives/login-header.directive';
export * from './directives/login-footer.directive'; export * from './directives/login-footer.directive';
export * from './components/login.component'; export * from './components/login/login.component';
export * from './components/login-dialog.component'; export * from './components/login-dialog/login-dialog.component';
export * from './components/login-dialog-component-data.interface'; export * from './components/login-dialog/login-dialog-component-data.interface';
export * from './components/login-dialog-panel.component'; export * from './components/login-dialog-panel/login-dialog-panel.component';
export * from './models/login-error.event'; export * from './models/login-error.event';
export * from './models/login-submit.event'; export * from './models/login-submit.event';