mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
[ACS-10117] Deprecate ADF Storybook and custom Docker builds
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
---
|
||||
Title: Login Dialog component
|
||||
Added: v2.6.0
|
||||
Status: Active
|
||||
Last reviewed: 2018-10-02
|
||||
---
|
||||
|
||||
# [Login Dialog component](../../../lib/core/src/lib/login/components/login-dialog.component.ts "Defined in login-dialog.component.ts")
|
||||
|
||||
This component is deprecated and will removed because it's unused.
|
||||
|
||||
Allows a user to perform a login via a dialog.
|
||||
|
||||
## Details
|
||||
|
||||
The [Login Dialog component](login-dialog.component.md) allows you to perform a login via a dialog.
|
||||
|
||||
### Showing the dialog
|
||||
|
||||
Unlike most components, the [Login Dialog Component](login-dialog.component.md) is typically shown in a dialog box
|
||||
rather than the main page and you are responsible for opening the dialog yourself. You can use the
|
||||
[Angular Material Dialog](https://material.angular.io/components/dialog/overview) for this,
|
||||
as shown in the usage example. ADF provides the [`LoginDialogComponentData`](../../../lib/core/src/lib/login/components/login-dialog-component-data.interface.ts) interface
|
||||
to work with the Dialog's
|
||||
[data option](https://material.angular.io/components/dialog/overview#sharing-data-with-the-dialog-component-):
|
||||
|
||||
```ts
|
||||
export interface LoginDialogComponentData {
|
||||
title: string;
|
||||
actionName?: string;
|
||||
logged: Subject<any>;
|
||||
}
|
||||
```
|
||||
|
||||
The properties are described in the table below:
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| title | `string` | "" | Dialog title |
|
||||
| actionName | `string` | "" | Text to appear on the dialog's main action button ("Login", "Access", etc) |
|
||||
| logged | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | | Event emitted when the login succeeds. |
|
||||
|
||||
If you don't want to manage the dialog yourself then it is easier to use the
|
||||
[Login Dialog Panel component](login-dialog-panel.component.md), or the
|
||||
methods of the [Login Dialog service](../services/login-dialog.service.md), which create
|
||||
the dialog for you.
|
||||
|
||||
### Usage example
|
||||
|
||||
```ts
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { LoginDialogComponentData, LoginDialogComponent} from '@adf/core'
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
...
|
||||
|
||||
constructor(dialog: MatDialog ... ) {}
|
||||
|
||||
openLoginDialog() {
|
||||
data: LoginDialogComponentData = {
|
||||
title: "Perform a Login",
|
||||
actionName: "Access",
|
||||
logged: new Subject<any>()
|
||||
};
|
||||
|
||||
this.dialog.open(
|
||||
LoginDialogComponent,
|
||||
{
|
||||
data, panelClass: 'adf-login-dialog',
|
||||
width: '630px'
|
||||
}
|
||||
);
|
||||
|
||||
data.logged.subscribe(() => {
|
||||
// Action after being logged in...
|
||||
},
|
||||
(error)=>{
|
||||
//your error handling
|
||||
},
|
||||
()=>{
|
||||
//action called when an action or cancel is clicked on the dialog
|
||||
this.dialog.closeAll();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
All the results will be streamed to the logged [subject](http://reactivex.io/rxjs/manual/overview.html#subject) present in the [`LoginDialogComponentData`](../../../lib/core/src/lib/login/components/login-dialog-component-data.interface.ts) object passed to the dialog.
|
||||
When the dialog action is selected by clicking, the `data.logged` stream will be completed.
|
||||
|
||||
## See also
|
||||
|
||||
- [Login component](login.component.md)
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
Title: Form Definition Selector Cloud
|
||||
Added: v3.3.0
|
||||
Status: Active
|
||||
---
|
||||
|
||||
# [Form Definition Selector Cloud](../../../lib/process-services-cloud/src/lib/form/components/form-definition-selector-cloud.component.ts "Defined in form-definition-selector-cloud.component.ts")
|
||||
|
||||
Allows one form to be selected from a dropdown list. For forms to be displayed in this component they will need to be compatible with standAlone tasks.
|
||||
|
||||
This component will be removed because it's unused.
|
||||
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<adf-cloud-form-definition-selector
|
||||
[appName]="'simple-app'"
|
||||
(selectForm)="onFormSelect($event)">
|
||||
</adf-cloud-form-definition-selector>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| appName | `string` | "" | Name of the application. If specified, this shows the users who have access to the app. |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| selectForm | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when a form is selected. |
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export interface LoginDialogComponentData {
|
||||
title: string;
|
||||
actionName?: string;
|
||||
logged: Subject<any>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<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)" />
|
||||
</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>
|
||||
@@ -0,0 +1,4 @@
|
||||
.adf-login-dialog-content adf-login .adf-login-content .adf-login-card-wide {
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Inject, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { LoginDialogPanelComponent } from '../login-dialog-panel/login-dialog-panel.component';
|
||||
import { LoginDialogComponentData } from './login-dialog-component-data.interface';
|
||||
|
||||
/** @deprecated this component will be removed because it's unused */
|
||||
@Component({
|
||||
selector: 'adf-login-dialog',
|
||||
templateUrl: './login-dialog.component.html',
|
||||
styleUrls: ['./login-dialog.component.scss'],
|
||||
imports: [MatDialogModule, LoginDialogPanelComponent, TranslatePipe, MatButtonModule],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class LoginDialogComponent {
|
||||
@ViewChild('adfLoginPanel', { static: true })
|
||||
loginPanel: LoginDialogPanelComponent;
|
||||
|
||||
buttonActionName = '';
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: LoginDialogComponentData) {
|
||||
this.buttonActionName = data.actionName ? `LOGIN.DIALOG.${data.actionName.toUpperCase()}` : 'LOGIN.DIALOG.CHOOSE';
|
||||
}
|
||||
|
||||
close() {
|
||||
this.data.logged.complete();
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
this.loginPanel.submitForm();
|
||||
}
|
||||
|
||||
onLoginSuccess(event: any) {
|
||||
this.data.logged.next(event);
|
||||
this.close();
|
||||
}
|
||||
|
||||
isFormValid() {
|
||||
return this.loginPanel ? this.loginPanel.isValid() : false;
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,19 @@
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { LoginDialogPanelComponent } from './components/login-dialog-panel/login-dialog-panel.component';
|
||||
import { LoginDialogComponent } from './components/login-dialog/login-dialog.component';
|
||||
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
import { LoginFooterDirective } from './directives/login-footer.directive';
|
||||
import { LoginHeaderDirective } from './directives/login-header.directive';
|
||||
|
||||
export const LOGIN_DIRECTIVES = [LoginComponent, LoginFooterDirective, LoginHeaderDirective, LoginDialogPanelComponent] as const;
|
||||
export const LOGIN_DIRECTIVES = [
|
||||
LoginComponent,
|
||||
LoginFooterDirective,
|
||||
LoginHeaderDirective,
|
||||
LoginDialogComponent,
|
||||
LoginDialogPanelComponent
|
||||
] as const;
|
||||
|
||||
/** @deprecated use `...LOGIN_DIRECTIVES` or import the standalone directives directly */
|
||||
@NgModule({
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<div class="adf-app-listgrid">
|
||||
<div class="adf-app-listgrid-item">
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class="adf-app-listgrid-item-card"
|
||||
title="{{applicationInstance.name}}"
|
||||
[ngClass]="getTheme()"
|
||||
(click)="onSelectApp(applicationInstance)"
|
||||
(keyup.enter)="onSelectApp(applicationInstance)">
|
||||
<div class="adf-app-listgrid-item-card-logo">
|
||||
<mat-icon class="adf-app-listgrid-item-card-logo-icon">{{ getIcon() }}</mat-icon>
|
||||
</div>
|
||||
<div mat-card-title class="adf-app-listgrid-item-card-title">
|
||||
<h1 class="adf-app-listgrid-item-card-title-text">{{applicationInstance.name}}</h1>
|
||||
</div>
|
||||
<mat-card-subtitle class="adf-app-listgrid-item-card-subtitle">
|
||||
<div class="adf-line-clamp">{{applicationInstance.description}}</div>
|
||||
</mat-card-subtitle>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/* stylelint-disable scss/no-global-function-names */
|
||||
@mixin adf-line-clamp($line-height: 1.25, $lines: 3) {
|
||||
position: relative;
|
||||
line-height: $line-height;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
/* stylelint-disable */
|
||||
@supports (-webkit-line-clamp: 1) {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: $lines;
|
||||
height: calc(0.99em * #{$line-height} * #{$lines});
|
||||
}
|
||||
|
||||
@supports not (-webkit-line-clamp: 1) {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
/* stylelint-enable */
|
||||
}
|
||||
|
||||
$tile-themes: (
|
||||
theme-1: (
|
||||
bg: #269abc,
|
||||
color: #168aac
|
||||
),
|
||||
theme-2: (
|
||||
bg: #7da9b0,
|
||||
color: #6d99a0
|
||||
),
|
||||
theme-3: (
|
||||
bg: #7689ab,
|
||||
color: #66799b
|
||||
),
|
||||
theme-4: (
|
||||
bg: #c74e3e,
|
||||
color: #b73e2e
|
||||
),
|
||||
theme-5: (
|
||||
bg: #fab96c,
|
||||
color: #eaa95c
|
||||
),
|
||||
theme-6: (
|
||||
bg: #759d4c,
|
||||
color: #658d3c
|
||||
),
|
||||
theme-7: (
|
||||
bg: #b1b489,
|
||||
color: #a1a479
|
||||
),
|
||||
theme-8: (
|
||||
bg: #a17299,
|
||||
color: #916289
|
||||
),
|
||||
theme-9: (
|
||||
bg: #696c67,
|
||||
color: #595c57
|
||||
),
|
||||
theme-10: (
|
||||
bg: #cabb33,
|
||||
color: #baab23
|
||||
)
|
||||
);
|
||||
|
||||
adf-cloud-app-details {
|
||||
.adf-app-listgrid {
|
||||
padding: 8px;
|
||||
display: block;
|
||||
|
||||
.adf-app-listgrid-item {
|
||||
outline: none;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
flex: unset;
|
||||
max-width: unset;
|
||||
|
||||
&-card {
|
||||
@for $i from 1 through 10 {
|
||||
&.theme-#{$i} {
|
||||
$tile-theme: map-get($tile-themes, theme-#{$i});
|
||||
|
||||
background-color: map-get($tile-theme, bg);
|
||||
|
||||
.adf-app-listgrid-item-card-logo-icon {
|
||||
color: map-get($tile-theme, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outline: none;
|
||||
transition:
|
||||
transform 280ms cubic-bezier(0.4, 0, 0.2, 1),
|
||||
box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
min-height: 200px;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
padding: 0;
|
||||
max-width: unset;
|
||||
|
||||
&:hover {
|
||||
box-shadow:
|
||||
0 8px 10px 1px rgba(0, 0, 0, 0.14),
|
||||
0 3px 14px 2px rgba(0, 0, 0, 0.12),
|
||||
0 5px 5px -3px rgba(0, 0, 0, 0.2);
|
||||
cursor: pointer;
|
||||
transform: scale(1.015);
|
||||
}
|
||||
|
||||
&-logo {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
padding: 16px;
|
||||
z-index: 9;
|
||||
|
||||
.adf-app-listgrid-item-card-logo-icon {
|
||||
font-size: 70px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&-title:has(.adf-app-listgrid-item-card-title-text) {
|
||||
padding: 16px;
|
||||
margin-bottom: 0;
|
||||
z-index: 9999;
|
||||
|
||||
h1 {
|
||||
color: white;
|
||||
width: 80%;
|
||||
font-size: var(--theme-headline-font-size);
|
||||
margin: 0;
|
||||
line-height: normal;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&-subtitle:has(.adf-line-clamp) {
|
||||
color: white;
|
||||
z-index: 9999;
|
||||
padding: 16px;
|
||||
flex: 1 0 auto;
|
||||
|
||||
.adf-line-clamp {
|
||||
@include adf-line-clamp(1.25, 3);
|
||||
}
|
||||
}
|
||||
|
||||
&-actions {
|
||||
padding: 0 16px 16px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
min-height: 48px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&-icon {
|
||||
color: #e9f1f3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { fakeApplicationInstance } from '../../mock/app-model.mock';
|
||||
import { AppDetailsCloudComponent } from './app-details-cloud.component';
|
||||
import { DEFAULT_APP_INSTANCE_THEME } from '../../models/application-instance.model';
|
||||
import { NoopTranslateModule } from '@alfresco/adf-core';
|
||||
|
||||
describe('AppDetailsCloudComponent', () => {
|
||||
let component: AppDetailsCloudComponent;
|
||||
let fixture: ComponentFixture<AppDetailsCloudComponent>;
|
||||
let host: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopTranslateModule, AppDetailsCloudComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(AppDetailsCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
host = fixture.nativeElement as HTMLElement;
|
||||
component.applicationInstance = fakeApplicationInstance[0];
|
||||
});
|
||||
|
||||
const getAppCard = () => host.querySelector<HTMLElement>('.adf-app-listgrid-item-card');
|
||||
|
||||
it('should display application name', () => {
|
||||
fixture.detectChanges();
|
||||
const appName = host.querySelector<HTMLDivElement>('.adf-app-listgrid-item-card-title');
|
||||
expect(appName.innerText.trim()).toEqual(fakeApplicationInstance[0].name);
|
||||
});
|
||||
|
||||
it('should emit a click event when app selected', () => {
|
||||
spyOn(component.selectedApp, 'emit');
|
||||
fixture.detectChanges();
|
||||
const app = getAppCard();
|
||||
app.click();
|
||||
expect(component.selectedApp.emit).toHaveBeenCalledWith(fakeApplicationInstance[0]);
|
||||
});
|
||||
|
||||
it('should render card with default icon and theme when are not provided', () => {
|
||||
component.applicationInstance = fakeApplicationInstance[2];
|
||||
fixture.detectChanges();
|
||||
|
||||
const card = getAppCard();
|
||||
expect(card.classList.contains(DEFAULT_APP_INSTANCE_THEME));
|
||||
|
||||
const icon = host.querySelector('.adf-app-listgrid-item-card-logo-icon');
|
||||
expect(icon).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render card with a non ApplicationInstanceModel input object', () => {
|
||||
component.applicationInstance = { name: 'application-new-3', createdAt: '2018-09-21T12:31:39.000Z', status: 'Pending' };
|
||||
fixture.detectChanges();
|
||||
const app = getAppCard();
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { ApplicationInstanceModel, DEFAULT_APP_INSTANCE_ICON, DEFAULT_APP_INSTANCE_THEME } from '../../models/application-instance.model';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
||||
/** @deprecated this component will be removed because it's unused */
|
||||
@Component({
|
||||
selector: 'adf-cloud-app-details',
|
||||
imports: [CommonModule, MatIconModule, MatCardModule],
|
||||
templateUrl: './app-details-cloud.component.html',
|
||||
styleUrls: ['./app-details-cloud.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AppDetailsCloudComponent {
|
||||
@Input({ required: true })
|
||||
applicationInstance: ApplicationInstanceModel;
|
||||
|
||||
@Output()
|
||||
selectedApp = new EventEmitter<ApplicationInstanceModel>();
|
||||
|
||||
/**
|
||||
* Pass the selected app as next
|
||||
*
|
||||
* @param app application model
|
||||
*/
|
||||
onSelectApp(app: ApplicationInstanceModel): void {
|
||||
this.selectedApp.emit(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application instance theme
|
||||
*
|
||||
* @returns the name of the theme
|
||||
*/
|
||||
getTheme(): string {
|
||||
return this.applicationInstance.theme || DEFAULT_APP_INSTANCE_THEME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application instance icon
|
||||
*
|
||||
* @returns the name of the icon
|
||||
*/
|
||||
getIcon(): string {
|
||||
return this.applicationInstance.icon || DEFAULT_APP_INSTANCE_ICON;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<div class="menu-container" *ngIf="apps$ | async as appsList; else loadingOrError">
|
||||
<ng-container *ngIf="appsList.length > 0; else noApps">
|
||||
<div
|
||||
*ngIf="isGrid(); else appList"
|
||||
class="adf-app-apps-grid">
|
||||
<adf-cloud-app-details
|
||||
*ngFor="let app of appsList"
|
||||
[applicationInstance]="app"
|
||||
(selectedApp)="onSelectApp($event)" />
|
||||
</div>
|
||||
|
||||
<ng-template #appList>
|
||||
<mat-list class="adf-app-list">
|
||||
<mat-list-item class="adf-app-list-item" (click)="onSelectApp(app)" (keyup.enter)="onSelectApp(app)"
|
||||
*ngFor="let app of appsList" tabindex="0" role="button" title="{{app.name}}">
|
||||
<mat-icon matListItemIcon>touch_app</mat-icon>
|
||||
<span class="adf-app-list-item-text" matLine>{{app.name}}</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #noApps>
|
||||
<div class="adf-app-list-empty">
|
||||
<ng-content select="adf-custom-empty-content-template" *ngIf="hasEmptyCustomContentTemplate; else defaultEmptyTemplate"
|
||||
class="adf-custom-empty-template" />
|
||||
|
||||
<ng-template #defaultEmptyTemplate>
|
||||
<adf-empty-content icon="apps" [title]="'ADF_CLOUD_TASK_LIST.APPS.NO_APPS.TITLE' | translate"
|
||||
[subtitle]="'ADF_CLOUD_TASK_LIST.APPS.NO_APPS.SUBTITLE' | translate" />
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #loadingOrError>
|
||||
<div *ngIf="loadingError$ | async; else loading" class="adf-app-list-error">
|
||||
<adf-empty-content icon="error_outline" [title]="'ADF_CLOUD_TASK_LIST.APPS.ERROR.TITLE' | translate"
|
||||
[subtitle]="'ADF_CLOUD_TASK_LIST.APPS.ERROR.SUBTITLE' | translate" />
|
||||
</div>
|
||||
<ng-template #loading>
|
||||
<ng-container>
|
||||
<div class="adf-app-list-spinner">
|
||||
<mat-spinner class="adf-app-list-cloud--spinner" />
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
@use '../../../flex' as flex;
|
||||
|
||||
adf-cloud-app-list {
|
||||
width: 100%;
|
||||
|
||||
.adf-app-list-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.adf-app-list-spinner,
|
||||
.adf-app-list-empty,
|
||||
.adf-app-list-error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
height: 85vh;
|
||||
|
||||
.adf-app-list-cloud-spinner {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.adf-app-apps-grid {
|
||||
flex-flow: row wrap;
|
||||
display: flex;
|
||||
|
||||
adf-cloud-app-details {
|
||||
flex: 1 1 100%;
|
||||
max-width: 33.3333%;
|
||||
|
||||
@include flex.layout-bp(lt-md) {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
@include flex.layout-bp(lt-sm) {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-content-services';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { fakeApplicationInstance } from '../../mock/app-model.mock';
|
||||
import { AppListCloudComponent, LAYOUT_GRID, LAYOUT_LIST } from './app-list-cloud.component';
|
||||
import { AppsProcessCloudService } from '../../services/apps-process-cloud.service';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { CustomEmptyContentTemplateDirective, NoopTranslateModule } from '@alfresco/adf-core';
|
||||
|
||||
describe('AppListCloudComponent', () => {
|
||||
let component: AppListCloudComponent;
|
||||
let fixture: ComponentFixture<AppListCloudComponent>;
|
||||
let appsProcessCloudService: AppsProcessCloudService;
|
||||
let getAppsSpy: jasmine.Spy;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
|
||||
const mock: any = {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(fakeApplicationInstance)
|
||||
},
|
||||
isLoggedIn: () => false,
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
@Component({
|
||||
imports: [MatIconModule, CustomEmptyContentTemplateDirective, AppListCloudComponent],
|
||||
template: `
|
||||
<adf-cloud-app-list>
|
||||
<adf-custom-empty-content-template>
|
||||
<mat-icon>apps</mat-icon>
|
||||
<p id="custom-id">No Apps Found</p>
|
||||
</adf-custom-empty-content-template>
|
||||
</adf-cloud-app-list>
|
||||
`
|
||||
})
|
||||
class CustomEmptyAppListCloudTemplateComponent {}
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopTranslateModule, AppListCloudComponent, CustomEmptyAppListCloudTemplateComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(AppListCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
appsProcessCloudService = TestBed.inject(AppsProcessCloudService);
|
||||
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
|
||||
getAppsSpy = spyOn(appsProcessCloudService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
|
||||
});
|
||||
|
||||
it('should define layoutType with the default value', () => {
|
||||
component.layoutType = '';
|
||||
fixture.detectChanges();
|
||||
expect(component.isGrid()).toBe(true);
|
||||
});
|
||||
|
||||
it('Should fetch deployed apps', (done) => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
component.apps$.subscribe((response: any[]) => {
|
||||
expect(response).toBeDefined();
|
||||
expect(response.length).toEqual(3);
|
||||
expect(response[0].name).toEqual('application-new-1');
|
||||
expect(response[0].status).toEqual('Deployed');
|
||||
expect(response[0].icon).toEqual('favorite_border');
|
||||
expect(response[0].theme).toEqual('theme-2');
|
||||
expect(response[1].name).toEqual('application-new-2');
|
||||
expect(response[1].status).toEqual('Pending');
|
||||
expect(response[1].icon).toEqual('favorite_border');
|
||||
expect(response[1].theme).toEqual('theme-2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should display default adf-empty-content template when response empty', () => {
|
||||
getAppsSpy.and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
const defaultEmptyTemplate = fixture.nativeElement.querySelector('.adf-app-list-empty');
|
||||
const emptyContent = fixture.debugElement.nativeElement.querySelector('.adf-empty-content');
|
||||
const emptyTitle = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__title');
|
||||
const emptySubtitle = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__subtitle');
|
||||
expect(defaultEmptyTemplate).toBeDefined();
|
||||
expect(defaultEmptyTemplate).not.toBeNull();
|
||||
expect(emptyContent).not.toBeNull();
|
||||
expect(emptyTitle.innerText).toBe('ADF_CLOUD_TASK_LIST.APPS.NO_APPS.TITLE');
|
||||
expect(emptySubtitle.innerText).toBe('ADF_CLOUD_TASK_LIST.APPS.NO_APPS.SUBTITLE');
|
||||
expect(getAppsSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should display default no permissions template when response returns exception', (done) => {
|
||||
getAppsSpy.and.returnValue(throwError({}));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
component.loadingError$.next(true);
|
||||
fixture.detectChanges();
|
||||
const errorTemplate = fixture.nativeElement.querySelector('.adf-app-list-error');
|
||||
const errorTitle = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__title');
|
||||
const errorSubtitle = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__subtitle');
|
||||
expect(errorTemplate).not.toBeNull();
|
||||
expect(errorTitle.innerText).toBe('ADF_CLOUD_TASK_LIST.APPS.ERROR.TITLE');
|
||||
expect(errorSubtitle.innerText).toBe('ADF_CLOUD_TASK_LIST.APPS.ERROR.SUBTITLE');
|
||||
expect(getAppsSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Grid Layout ', () => {
|
||||
it('should display a grid by default', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.isGrid()).toBe(true);
|
||||
expect(component.isList()).toBe(false);
|
||||
});
|
||||
|
||||
it('should defined adf-cloud-app-details when layout type is grid', () => {
|
||||
fixture.detectChanges();
|
||||
const adfCloudDetailsElement = fixture.nativeElement.querySelectorAll('adf-cloud-app-details');
|
||||
const appName = fixture.nativeElement.querySelector('.adf-app-listgrid-item-card-title');
|
||||
expect(adfCloudDetailsElement).toBeDefined();
|
||||
expect(adfCloudDetailsElement).not.toBeNull();
|
||||
|
||||
expect(adfCloudDetailsElement.length).toEqual(3);
|
||||
expect(component.isGrid()).toBe(true);
|
||||
expect(component.isList()).toBe(false);
|
||||
|
||||
expect(appName.innerText.trim()).toEqual(fakeApplicationInstance[0].name);
|
||||
});
|
||||
|
||||
it('should display a grid when configured to', () => {
|
||||
component.layoutType = LAYOUT_GRID;
|
||||
fixture.detectChanges();
|
||||
expect(component.isGrid()).toBe(true);
|
||||
expect(component.isList()).toBe(false);
|
||||
});
|
||||
|
||||
it('should throw an exception on init if unknown type configured', () => {
|
||||
component.layoutType = 'unknown';
|
||||
expect(component.ngOnInit).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('List Layout ', () => {
|
||||
beforeEach(() => {
|
||||
component.layoutType = LAYOUT_LIST;
|
||||
});
|
||||
|
||||
it('should display a LIST when configured to', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.isGrid()).toBe(false);
|
||||
expect(component.isList()).toBe(true);
|
||||
});
|
||||
|
||||
it('should display list when layout type is LIST', () => {
|
||||
fixture.detectChanges();
|
||||
const appListElement = fixture.nativeElement.querySelectorAll('.adf-app-list');
|
||||
const appListItemElement = fixture.nativeElement.querySelectorAll('.adf-app-list-item');
|
||||
const appName = fixture.nativeElement.querySelector('.adf-app-list-item-text');
|
||||
expect(appListElement).toBeDefined();
|
||||
expect(appListElement).not.toBeNull();
|
||||
|
||||
expect(appListItemElement.length).toEqual(3);
|
||||
expect(component.isGrid()).toBe(false);
|
||||
expect(component.isList()).toBe(true);
|
||||
|
||||
expect(appName.innerText.trim()).toEqual(fakeApplicationInstance[0].name);
|
||||
});
|
||||
|
||||
it('should throw an exception on init if unknown type configured', () => {
|
||||
component.layoutType = 'unknown';
|
||||
expect(component.ngOnInit).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit a click event when app selected', () => {
|
||||
spyOn(component.appClick, 'emit');
|
||||
fixture.detectChanges();
|
||||
const onAppClick = fixture.nativeElement.querySelector('.adf-app-listgrid-item-card');
|
||||
onAppClick.click();
|
||||
expect(component.appClick.emit).toHaveBeenCalledWith(fakeApplicationInstance[0]);
|
||||
});
|
||||
|
||||
describe('Custom CustomEmptyAppListCloudTemplateComponent', () => {
|
||||
let customFixture: ComponentFixture<CustomEmptyAppListCloudTemplateComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
getAppsSpy.and.returnValue(of([]));
|
||||
customFixture = TestBed.createComponent(CustomEmptyAppListCloudTemplateComponent);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
customFixture.destroy();
|
||||
});
|
||||
|
||||
it('should render the custom empty template', async () => {
|
||||
customFixture.detectChanges();
|
||||
await customFixture.whenStable();
|
||||
|
||||
const title = customFixture.nativeElement.querySelector('#custom-id');
|
||||
expect(title.innerText).toBe('No Apps Found');
|
||||
});
|
||||
});
|
||||
});
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CustomEmptyContentTemplateDirective, EmptyContentComponent } from '@alfresco/adf-core';
|
||||
import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { AppsProcessCloudService } from '../../services/apps-process-cloud.service';
|
||||
import { ApplicationInstanceModel } from '../../models/application-instance.model';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { AppDetailsCloudComponent } from '../app-details-cloud/app-details-cloud.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatLineModule } from '@angular/material/core';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
export const LAYOUT_LIST: string = 'LIST';
|
||||
export const LAYOUT_GRID: string = 'GRID';
|
||||
export const DEPLOYED_STATUS: string = 'DEPLOYED';
|
||||
|
||||
/** @deprecated this component will be removed because it's unused */
|
||||
@Component({
|
||||
selector: 'adf-cloud-app-list',
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslatePipe,
|
||||
AppDetailsCloudComponent,
|
||||
MatIconModule,
|
||||
MatLineModule,
|
||||
MatListModule,
|
||||
EmptyContentComponent,
|
||||
MatProgressSpinnerModule
|
||||
],
|
||||
templateUrl: './app-list-cloud.component.html',
|
||||
styleUrls: ['./app-list-cloud.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AppListCloudComponent implements OnInit, AfterContentInit {
|
||||
@ContentChild(CustomEmptyContentTemplateDirective)
|
||||
emptyCustomContent: CustomEmptyContentTemplateDirective;
|
||||
|
||||
/**
|
||||
* Defines the layout of the apps. There are two possible
|
||||
* values, "GRID" and "LIST".
|
||||
*/
|
||||
@Input()
|
||||
layoutType: string = LAYOUT_GRID;
|
||||
|
||||
/** Emitted when an app entry is clicked. */
|
||||
@Output()
|
||||
appClick = new EventEmitter<ApplicationInstanceModel>();
|
||||
|
||||
apps$: Observable<any>;
|
||||
loadingError$ = new Subject<boolean>();
|
||||
hasEmptyCustomContentTemplate: boolean = false;
|
||||
|
||||
constructor(private appsProcessCloudService: AppsProcessCloudService) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.isValidType()) {
|
||||
this.setDefaultLayoutType();
|
||||
}
|
||||
|
||||
this.apps$ = this.appsProcessCloudService.getDeployedApplicationsByStatus(DEPLOYED_STATUS).pipe(
|
||||
catchError(() => {
|
||||
this.loadingError$.next(true);
|
||||
return of();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.emptyCustomContent) {
|
||||
this.hasEmptyCustomContentTemplate = true;
|
||||
}
|
||||
}
|
||||
|
||||
onSelectApp(app: ApplicationInstanceModel): void {
|
||||
this.appClick.emit(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value of the layoutType property is an allowed value
|
||||
*
|
||||
* @returns `true` if layout type is valid, otherwise `false`
|
||||
*/
|
||||
isValidType(): boolean {
|
||||
if (this.layoutType && (this.layoutType === LAYOUT_LIST || this.layoutType === LAYOUT_GRID)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the default value to LayoutType
|
||||
*/
|
||||
setDefaultLayoutType(): void {
|
||||
this.layoutType = LAYOUT_GRID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the layout type is LIST
|
||||
*
|
||||
* @returns `true` if the layout is list, otherwise `false`
|
||||
*/
|
||||
isList(): boolean {
|
||||
return this.layoutType === LAYOUT_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the layout type is GRID
|
||||
*
|
||||
* @returns `true` if layout is grid, otherwise `false`
|
||||
*/
|
||||
isGrid(): boolean {
|
||||
return this.layoutType === LAYOUT_GRID;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const DEFAULT_APP_INSTANCE_THEME = 'theme-2';
|
||||
export const DEFAULT_APP_INSTANCE_ICON = 'favorite_border';
|
||||
|
||||
export interface ApplicationInstanceModel {
|
||||
name?: string;
|
||||
displayName?: string;
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<mat-form-field class="adf-form-definition-selector">
|
||||
<mat-label>{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.FORM' | translate}}</mat-label>
|
||||
<mat-select class="adf-form-selector-dropdown" (selectionChange)="onSelect($event)">
|
||||
<mat-option [value]="''">{{'ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE' | translate}}</mat-option>
|
||||
<mat-option *ngFor="let form of forms$ | async" [value]="form.id">{{ form.name }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
.adf {
|
||||
&-form-definition-selector {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormDefinitionSelectorCloudComponent } from './form-definition-selector-cloud.component';
|
||||
import { of } from 'rxjs';
|
||||
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||
import { NoopTranslateModule } from '@alfresco/adf-core';
|
||||
|
||||
describe('FormDefinitionCloudComponent', () => {
|
||||
let fixture: ComponentFixture<FormDefinitionSelectorCloudComponent>;
|
||||
let service: FormDefinitionSelectorCloudService;
|
||||
let getFormsSpy: jasmine.Spy;
|
||||
let loader: HarnessLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopTranslateModule, FormDefinitionSelectorCloudComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(FormDefinitionSelectorCloudComponent);
|
||||
service = TestBed.inject(FormDefinitionSelectorCloudService);
|
||||
getFormsSpy = spyOn(service, 'getStandAloneTaskForms').and.returnValue(of([{ id: 'fake-form', name: 'fakeForm' } as any]));
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
});
|
||||
|
||||
it('should load the forms by default', async () => {
|
||||
const selectElement = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-form-selector-dropdown' }));
|
||||
await selectElement.open();
|
||||
const options = await selectElement.getOptions();
|
||||
|
||||
expect(options.length).toBe(2);
|
||||
expect(await options[0].getText()).toBe('ADF_CLOUD_TASK_LIST.START_TASK.FORM.LABEL.NONE');
|
||||
expect(await options[1].getText()).toBe('fakeForm');
|
||||
expect(getFormsSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should load only None option when no forms exist', async () => {
|
||||
getFormsSpy.and.returnValue(of([]));
|
||||
const selectElement = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-form-selector-dropdown' }));
|
||||
await selectElement.open();
|
||||
|
||||
const options = await selectElement.getOptions();
|
||||
|
||||
expect(options.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not preselect any form by default', async () => {
|
||||
const selectElement = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-form-selector-dropdown' }));
|
||||
|
||||
expect(await selectElement.getValueText()).toBe('');
|
||||
});
|
||||
|
||||
it('should display the name of the form that is selected', async () => {
|
||||
const selectElement = await loader.getHarness(MatSelectHarness.with({ selector: '.adf-form-selector-dropdown' }));
|
||||
await selectElement.open();
|
||||
const options = await selectElement.getOptions();
|
||||
|
||||
await options[1].click();
|
||||
|
||||
expect(await selectElement.getValueText()).toBe('fakeForm');
|
||||
});
|
||||
});
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FormDefinitionSelectorCloudService } from '../services/form-definition-selector-cloud.service';
|
||||
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
|
||||
import { FormRepresentation } from '../../services/form-fields.interfaces';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
|
||||
/** @deprecated this component will be removed because it's unused */
|
||||
@Component({
|
||||
selector: 'adf-cloud-form-definition-selector',
|
||||
imports: [CommonModule, TranslatePipe, MatSelectModule],
|
||||
templateUrl: './form-definition-selector-cloud.component.html',
|
||||
styleUrls: ['./form-definition-selector-cloud.component.scss']
|
||||
})
|
||||
export class FormDefinitionSelectorCloudComponent implements OnInit {
|
||||
/** Name of the application. If specified, this shows the users who have access to the app. */
|
||||
@Input()
|
||||
appName: string = '';
|
||||
|
||||
/** Emitted when a form is selected. */
|
||||
@Output()
|
||||
selectForm: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
forms$: Observable<FormRepresentation[]>;
|
||||
|
||||
constructor(private formDefinitionCloudService: FormDefinitionSelectorCloudService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.forms$ = this.formDefinitionCloudService.getStandAloneTaskForms(this.appName);
|
||||
}
|
||||
|
||||
onSelect(event: MatSelectChange) {
|
||||
this.selectForm.emit(event.value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const mockFormRepresentations = [
|
||||
{
|
||||
formRepresentation: {
|
||||
id: 'form-de8895be-d0d7-4434-beef-559b15305d72',
|
||||
name: 'Form 1',
|
||||
description: '',
|
||||
version: 0,
|
||||
standalone: true
|
||||
}
|
||||
},
|
||||
{
|
||||
formRepresentation: {
|
||||
id: 'form-de8895be-d0d7-4434-beef-fgr34ttgrtgd',
|
||||
name: 'Form 2',
|
||||
description: '',
|
||||
version: 0,
|
||||
standalone: false
|
||||
}
|
||||
},
|
||||
{
|
||||
formRepresentation: {
|
||||
id: 'form-de8895be-d0d7-4434-beef-53453453452',
|
||||
name: 'Form 3',
|
||||
description: '',
|
||||
version: 0
|
||||
}
|
||||
}
|
||||
];
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { FormDefinitionSelectorCloudService } from './form-definition-selector-cloud.service';
|
||||
import { mockFormRepresentations } from '../mocks/form-representation.mock';
|
||||
import { AdfHttpClient } from '@alfresco/adf-core/api';
|
||||
import { NoopTranslateModule } from '@alfresco/adf-core';
|
||||
|
||||
describe('Form Definition Selector Cloud Service', () => {
|
||||
let service: FormDefinitionSelectorCloudService;
|
||||
let adfHttpClient: AdfHttpClient;
|
||||
const appName = 'app-name';
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopTranslateModule]
|
||||
});
|
||||
service = TestBed.inject(FormDefinitionSelectorCloudService);
|
||||
adfHttpClient = TestBed.inject(AdfHttpClient);
|
||||
spyOn(adfHttpClient, 'request').and.returnValue(Promise.resolve(mockFormRepresentations));
|
||||
});
|
||||
|
||||
it('should fetch all the forms when getForms is called', (done) => {
|
||||
service.getForms(appName).subscribe((result) => {
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBe(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch only standalone enabled forms when getStandaloneTaskForms is called', (done) => {
|
||||
service.getStandAloneTaskForms(appName).subscribe((result) => {
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toBe(2);
|
||||
expect(result[0].name).toBe('Form 1');
|
||||
expect(result[1].name).toBe('Form 3');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { BaseCloudService } from '../../services/base-cloud.service';
|
||||
import { FormRepresentation } from '../../services/form-fields.interfaces';
|
||||
import { FormDefinitionSelectorCloudServiceInterface } from './form-definition-selector-cloud.service.interface';
|
||||
|
||||
/** @deprecated this service will be removed because it's component is unused */
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FormDefinitionSelectorCloudService extends BaseCloudService implements FormDefinitionSelectorCloudServiceInterface {
|
||||
/**
|
||||
* Get all forms of an app.
|
||||
*
|
||||
* @param appName Name of the application
|
||||
* @returns Details of the forms
|
||||
*/
|
||||
getForms(appName: string): Observable<FormRepresentation[]> {
|
||||
const url = `${this.getBasePath(appName)}/form/v1/forms`;
|
||||
|
||||
return this.get(url).pipe(map((data: any) => data.map((formData: any) => formData.formRepresentation)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all forms of an app.
|
||||
*
|
||||
* @param appName Name of the application
|
||||
* @returns Details of the forms
|
||||
*/
|
||||
getStandAloneTaskForms(appName: string): Observable<FormRepresentation[]> {
|
||||
return from(this.getForms(appName)).pipe(
|
||||
map((data: any) => data.filter((formData: any) => formData.standalone || formData.standalone === undefined))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||
import { provideTranslations } from '@alfresco/adf-core';
|
||||
import { APP_LIST_CLOUD_DIRECTIVES } from './app/app-list-cloud.module';
|
||||
import { TaskCloudModule } from './task/task-cloud.module';
|
||||
import { ProcessCloudModule } from './process/process-cloud.module';
|
||||
import { FORM_CLOUD_DIRECTIVES } from './form/form-cloud.module';
|
||||
@@ -27,7 +28,12 @@ import { PeopleCloudComponent } from './people/components/people-cloud.component
|
||||
import { provideCloudFormRenderer, provideCloudPreferences } from './providers';
|
||||
import { TaskListCloudService } from './task/task-list/services/task-list-cloud.service';
|
||||
|
||||
export const PROCESS_SERVICES_CLOUD_DIRECTIVES = [...FORM_CLOUD_DIRECTIVES, ...TASK_FORM_CLOUD_DIRECTIVES, PeopleCloudComponent] as const;
|
||||
export const PROCESS_SERVICES_CLOUD_DIRECTIVES = [
|
||||
...APP_LIST_CLOUD_DIRECTIVES,
|
||||
...FORM_CLOUD_DIRECTIVES,
|
||||
...TASK_FORM_CLOUD_DIRECTIVES,
|
||||
PeopleCloudComponent
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* @deprecated this module is deprecated and will be removed in the future versions
|
||||
|
||||
Reference in New Issue
Block a user