ACS-8251: migrate Extensions library to Standalone (#9842)

This commit is contained in:
Denys Vuika 2024-06-20 08:52:30 -04:00 committed by GitHub
parent 45e921a382
commit 9a544307d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 465 additions and 572 deletions

View File

@ -22,7 +22,9 @@ import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@Component({ @Component({
template: `<div tabindex="0" adf-auto-focus> Test</div>` standalone: true,
imports: [AutoFocusDirective],
template: ` <div tabindex="0" adf-auto-focus>Test</div>`
}) })
class AutoFocusTestComponent {} class AutoFocusTestComponent {}
@ -31,13 +33,7 @@ describe('AutoFocusDirective', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [TranslateModule.forRoot(), AutoFocusDirective, AutoFocusTestComponent]
TranslateModule.forRoot()
],
declarations: [
AutoFocusDirective,
AutoFocusTestComponent
]
}); });
fixture = TestBed.createComponent(AutoFocusTestComponent); fixture = TestBed.createComponent(AutoFocusTestComponent);
}); });

View File

@ -18,14 +18,15 @@
import { AfterContentInit, Directive, ElementRef } from '@angular/core'; import { AfterContentInit, Directive, ElementRef } from '@angular/core';
@Directive({ @Directive({
selector: '[adf-auto-focus]' standalone: true,
selector: '[adf-auto-focus]'
}) })
export class AutoFocusDirective implements AfterContentInit { export class AutoFocusDirective implements AfterContentInit {
public constructor(private el: ElementRef) {} public constructor(private el: ElementRef) {}
public ngAfterContentInit() { public ngAfterContentInit() {
setTimeout(() => { setTimeout(() => {
this.el.nativeElement.focus(); this.el.nativeElement.focus();
}, 100); }, 100);
} }
} }

View File

@ -17,37 +17,39 @@
/* eslint-disable @angular-eslint/no-input-rename */ /* eslint-disable @angular-eslint/no-input-rename */
import { ChangeDetectorRef, Directive, ElementRef, Host, Inject, Input, OnChanges, Optional, Renderer2, SimpleChanges } from '@angular/core'; import { ChangeDetectorRef, Directive, ElementRef, Host, Inject, Input, OnChanges, Optional, Renderer2, SimpleChanges } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api'; import { NodeEntry } from '@alfresco/js-api';
import { EXTENDIBLE_COMPONENT } from '@alfresco/adf-core'; import { EXTENDIBLE_COMPONENT } from '@alfresco/adf-core';
import { ContentService } from '../common/services/content.service'; import { ContentService } from '../common/services/content.service';
import { NodeAllowableOperationSubject } from '../interfaces/node-allowable-operation-subject.interface'; import { NodeAllowableOperationSubject } from '../interfaces/node-allowable-operation-subject.interface';
@Directive({ @Directive({
standalone: true,
selector: '[adf-check-allowable-operation]' selector: '[adf-check-allowable-operation]'
}) })
export class CheckAllowableOperationDirective implements OnChanges { export class CheckAllowableOperationDirective implements OnChanges {
/** /**
* Node permission to check (create, delete, update, updatePermissions, * Node permission to check (create, delete, update, updatePermissions,
* !create, !delete, !update, !updatePermissions). * !create, !delete, !update, !updatePermissions).
*/ */
@Input('adf-check-allowable-operation') @Input('adf-check-allowable-operation')
permission: string = null; permission: string = null;
/** Nodes to check permission for. */ /** Nodes to check permission for. */
@Input('adf-nodes') @Input('adf-nodes')
nodes: NodeEntry[] = []; nodes: NodeEntry[] = [];
constructor(private elementRef: ElementRef, constructor(
private renderer: Renderer2, private elementRef: ElementRef,
private contentService: ContentService, private renderer: Renderer2,
private changeDetector: ChangeDetectorRef, private contentService: ContentService,
private changeDetector: ChangeDetectorRef,
@Host() @Host()
@Optional() @Optional()
@Inject(EXTENDIBLE_COMPONENT) private parentComponent?: NodeAllowableOperationSubject) { @Inject(EXTENDIBLE_COMPONENT)
} private parentComponent?: NodeAllowableOperationSubject
) {}
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes.nodes && !changes.nodes.firstChange) { if (changes.nodes && !changes.nodes.firstChange) {

View File

@ -15,11 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { MaterialModule } from '../material.module';
import { TranslateModule } from '@ngx-translate/core';
import { NodeLockDirective } from './node-lock.directive'; import { NodeLockDirective } from './node-lock.directive';
import { NodeCounterComponent, NodeCounterDirective } from './node-counter.directive'; import { NodeCounterComponent, NodeCounterDirective } from './node-counter.directive';
import { AutoFocusDirective } from './auto-focus.directive'; import { AutoFocusDirective } from './auto-focus.directive';
@ -31,37 +27,23 @@ import { NodeFavoriteDirective } from './node-favorite.directive';
import { NodeRestoreDirective } from './node-restore.directive'; import { NodeRestoreDirective } from './node-restore.directive';
import { NodeDownloadDirective } from './node-download.directive'; import { NodeDownloadDirective } from './node-download.directive';
export const CONTENT_DIRECTIVES = [
NodeLockDirective,
NodeCounterDirective,
NodeCounterComponent,
AutoFocusDirective,
CheckAllowableOperationDirective,
LibraryFavoriteDirective,
LibraryMembershipDirective,
NodeDeleteDirective,
NodeFavoriteDirective,
NodeRestoreDirective,
NodeDownloadDirective
];
/** @deprecated import CONTENT_DIRECTIVES or standalone directives instead */
@NgModule({ @NgModule({
imports: [ imports: [...CONTENT_DIRECTIVES],
CommonModule, exports: [...CONTENT_DIRECTIVES]
MaterialModule,
TranslateModule
],
declarations: [
NodeLockDirective,
NodeCounterDirective,
NodeCounterComponent,
AutoFocusDirective,
CheckAllowableOperationDirective,
LibraryFavoriteDirective,
LibraryMembershipDirective,
NodeDeleteDirective,
NodeFavoriteDirective,
NodeRestoreDirective,
NodeDownloadDirective
],
exports: [
NodeLockDirective,
NodeCounterDirective,
AutoFocusDirective,
CheckAllowableOperationDirective,
LibraryFavoriteDirective,
LibraryMembershipDirective,
NodeDeleteDirective,
NodeFavoriteDirective,
NodeRestoreDirective,
NodeDownloadDirective
]
}) })
export class ContentDirectiveModule { export class ContentDirectiveModule {}
}

View File

@ -22,8 +22,10 @@ import { CoreTestingModule } from '@alfresco/adf-core';
import { LibraryEntity } from '../interfaces/library-entity.interface'; import { LibraryEntity } from '../interfaces/library-entity.interface';
@Component({ @Component({
standalone: true,
imports: [LibraryFavoriteDirective],
selector: 'app-test-component', selector: 'app-test-component',
template: ` <button #favoriteLibrary="favoriteLibrary" [adf-favorite-library]="selection">Favorite</button> ` template: `<button #favoriteLibrary="favoriteLibrary" [adf-favorite-library]="selection">Favorite</button>`
}) })
class TestComponent { class TestComponent {
@ViewChild('favoriteLibrary', { static: true }) @ViewChild('favoriteLibrary', { static: true })
@ -39,8 +41,7 @@ describe('LibraryFavoriteDirective', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CoreTestingModule], imports: [CoreTestingModule, LibraryFavoriteDirective, TestComponent]
declarations: [TestComponent, LibraryFavoriteDirective]
}); });
fixture = TestBed.createComponent(TestComponent); fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -21,6 +21,7 @@ import { AlfrescoApiService } from '@alfresco/adf-core';
import { LibraryEntity } from '../interfaces/library-entity.interface'; import { LibraryEntity } from '../interfaces/library-entity.interface';
@Directive({ @Directive({
standalone: true,
selector: '[adf-favorite-library]', selector: '[adf-favorite-library]',
exportAs: 'favoriteLibrary' exportAs: 'favoriteLibrary'
}) })

View File

@ -25,6 +25,7 @@ import { VersionCompatibilityService } from '../version-compatibility/version-co
import { SitesService } from '../common/services/sites.service'; import { SitesService } from '../common/services/sites.service';
@Directive({ @Directive({
standalone: true,
selector: '[adf-library-membership]', selector: '[adf-library-membership]',
exportAs: 'libraryMembership' exportAs: 'libraryMembership'
}) })

View File

@ -22,6 +22,8 @@ import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@Component({ @Component({
standalone: true,
imports: [NodeCounterDirective],
template: `<div [adf-node-counter]="count"></div>` template: `<div [adf-node-counter]="count"></div>`
}) })
class TestComponent { class TestComponent {
@ -33,14 +35,7 @@ describe('NodeCounterDirective', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [TranslateModule.forRoot(), NodeCounterDirective, NodeCounterComponent, TestComponent]
TranslateModule.forRoot()
],
declarations: [
NodeCounterDirective,
NodeCounterComponent,
TestComponent
]
}); });
fixture = TestBed.createComponent(TestComponent); fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges(); fixture.detectChanges();

View File

@ -16,8 +16,10 @@
*/ */
import { Directive, Input, Component, OnInit, OnChanges, ViewContainerRef } from '@angular/core'; import { Directive, Input, Component, OnInit, OnChanges, ViewContainerRef } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
@Directive({ @Directive({
standalone: true,
selector: '[adf-node-counter]' selector: '[adf-node-counter]'
}) })
export class NodeCounterDirective implements OnInit, OnChanges { export class NodeCounterDirective implements OnInit, OnChanges {
@ -42,6 +44,8 @@ export class NodeCounterDirective implements OnInit, OnChanges {
} }
@Component({ @Component({
standalone: true,
imports: [TranslateModule],
selector: 'adf-node-counter', selector: 'adf-node-counter',
template: ` <div>{{ 'NODE_COUNTER.SELECTED_COUNT' | translate : { count: counter } }}</div> ` template: ` <div>{{ 'NODE_COUNTER.SELECTED_COUNT' | translate : { count: counter } }}</div> `
}) })

View File

@ -23,7 +23,7 @@ import { CoreTestingModule } from '@alfresco/adf-core';
import { ContentDirectiveModule } from './content-directive.module'; import { ContentDirectiveModule } from './content-directive.module';
@Component({ @Component({
template: ` <div id="delete-component" [adf-delete]="selection" (delete)="onDelete()"></div>` template: `<div id="delete-component" [adf-delete]="selection" (delete)="onDelete()"></div>`
}) })
class TestComponent { class TestComponent {
selection = []; selection = [];
@ -35,7 +35,7 @@ class TestComponent {
} }
@Component({ @Component({
template: ` <div id="delete-component" [adf-check-allowable-operation]="selection" [adf-delete]="selection" (delete)="onDelete($event)"></div>` template: `<div id="delete-component" [adf-check-allowable-operation]="selection" [adf-delete]="selection" (delete)="onDelete($event)"></div>`
}) })
class TestWithPermissionsComponent { class TestWithPermissionsComponent {
selection = []; selection = [];

View File

@ -46,6 +46,7 @@ interface ProcessStatus {
} }
@Directive({ @Directive({
standalone: true,
selector: '[adf-delete]' selector: '[adf-delete]'
}) })
export class NodeDeleteDirective implements OnChanges { export class NodeDeleteDirective implements OnChanges {
@ -61,13 +62,13 @@ export class NodeDeleteDirective implements OnChanges {
@Output() @Output()
delete: EventEmitter<any> = new EventEmitter(); delete: EventEmitter<any> = new EventEmitter();
_trashcanApi: TrashcanApi; private _trashcanApi: TrashcanApi;
get trashcanApi(): TrashcanApi { get trashcanApi(): TrashcanApi {
this._trashcanApi = this._trashcanApi ?? new TrashcanApi(this.alfrescoApiService.getInstance()); this._trashcanApi = this._trashcanApi ?? new TrashcanApi(this.alfrescoApiService.getInstance());
return this._trashcanApi; return this._trashcanApi;
} }
_nodesApi: NodesApi; private _nodesApi: NodesApi;
get nodesApi(): NodesApi { get nodesApi(): NodesApi {
this._nodesApi = this._nodesApi ?? new NodesApi(this.alfrescoApiService.getInstance()); this._nodesApi = this._nodesApi ?? new NodesApi(this.alfrescoApiService.getInstance());
return this._nodesApi; return this._nodesApi;

View File

@ -25,11 +25,12 @@ import { ContentApi, NodeEntry, VersionEntry } from '@alfresco/js-api';
* Directive selectors without adf- prefix will be deprecated on 3.0.0 * Directive selectors without adf- prefix will be deprecated on 3.0.0
*/ */
@Directive({ @Directive({
standalone: true,
// eslint-disable-next-line @angular-eslint/directive-selector // eslint-disable-next-line @angular-eslint/directive-selector
selector: '[adfNodeDownload]' selector: '[adfNodeDownload]'
}) })
export class NodeDownloadDirective { export class NodeDownloadDirective {
_contentApi: ContentApi; private _contentApi: ContentApi;
get contentApi(): ContentApi { get contentApi(): ContentApi {
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance()); this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
return this._contentApi; return this._contentApi;

View File

@ -24,6 +24,7 @@ import { catchError, map } from 'rxjs/operators';
import { AlfrescoApiService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '@alfresco/adf-core';
@Directive({ @Directive({
standalone: true,
selector: '[adf-node-favorite]', selector: '[adf-node-favorite]',
exportAs: 'adfFavorite' exportAs: 'adfFavorite'
}) })

View File

@ -24,15 +24,15 @@ import { AllowableOperationsEnum } from '../common/models/allowable-operations.e
import { ContentNodeDialogService } from '../content-node-selector/content-node-dialog.service'; import { ContentNodeDialogService } from '../content-node-selector/content-node-dialog.service';
@Directive({ @Directive({
standalone: true,
selector: '[adf-node-lock]' selector: '[adf-node-lock]'
}) })
export class NodeLockDirective implements AfterViewInit { export class NodeLockDirective implements AfterViewInit {
/** Node to lock/unlock. */ /** Node to lock/unlock. */
@Input('adf-node-lock') @Input('adf-node-lock')
node: Node; node: Node;
@HostListener('click', [ '$event' ]) @HostListener('click', ['$event'])
onClick(event) { onClick(event) {
event.stopPropagation(); event.stopPropagation();
this.contentNodeDialogService.openLockNodeDialog(this.node); this.contentNodeDialogService.openLockNodeDialog(this.node);

View File

@ -18,20 +18,20 @@
/* eslint-disable @angular-eslint/component-selector, @angular-eslint/no-input-rename */ /* eslint-disable @angular-eslint/component-selector, @angular-eslint/no-input-rename */
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core'; import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
import { import { TrashcanApi, DeletedNodeEntry, DeletedNodesPaging } from '@alfresco/js-api';
TrashcanApi, DeletedNodeEntry, DeletedNodesPaging } from '@alfresco/js-api';
import { Observable, forkJoin, from, of } from 'rxjs'; import { Observable, forkJoin, from, of } from 'rxjs';
import { tap, mergeMap, map, catchError } from 'rxjs/operators'; import { tap, mergeMap, map, catchError } from 'rxjs/operators';
import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core'; import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core';
import { RestoreMessageModel } from '../interfaces/restore-message-model.interface'; import { RestoreMessageModel } from '../interfaces/restore-message-model.interface';
@Directive({ @Directive({
standalone: true,
selector: '[adf-restore]' selector: '[adf-restore]'
}) })
export class NodeRestoreDirective { export class NodeRestoreDirective {
private readonly restoreProcessStatus; private readonly restoreProcessStatus;
_trashcanApi: TrashcanApi; private _trashcanApi: TrashcanApi;
get trashcanApi(): TrashcanApi { get trashcanApi(): TrashcanApi {
this._trashcanApi = this._trashcanApi ?? new TrashcanApi(this.alfrescoApiService.getInstance()); this._trashcanApi = this._trashcanApi ?? new TrashcanApi(this.alfrescoApiService.getInstance());
return this._trashcanApi; return this._trashcanApi;
@ -50,8 +50,7 @@ export class NodeRestoreDirective {
this.recover(this.selection); this.recover(this.selection);
} }
constructor(private alfrescoApiService: AlfrescoApiService, constructor(private alfrescoApiService: AlfrescoApiService, private translation: TranslationService) {
private translation: TranslationService) {
this.restoreProcessStatus = this.processStatus(); this.restoreProcessStatus = this.processStatus();
} }
@ -63,28 +62,28 @@ export class NodeRestoreDirective {
const nodesWithPath = this.getNodesWithPath(selection); const nodesWithPath = this.getNodesWithPath(selection);
if (selection.length && nodesWithPath.length) { if (selection.length && nodesWithPath.length) {
this.restoreNodesBatch(nodesWithPath)
.pipe(
tap((restoredNodes) => {
const status = this.processStatus(restoredNodes);
this.restoreNodesBatch(nodesWithPath).pipe( this.restoreProcessStatus.fail.push(...status.fail);
tap((restoredNodes) => { this.restoreProcessStatus.success.push(...status.success);
const status = this.processStatus(restoredNodes); }),
mergeMap(() => this.getDeletedNodes())
)
.subscribe((deletedNodesList) => {
const { entries: nodeList } = deletedNodesList.list;
const { fail: restoreErrorNodes } = this.restoreProcessStatus;
const selectedNodes = this.diff(restoreErrorNodes, selection, false);
const remainingNodes = this.diff(selectedNodes, nodeList);
this.restoreProcessStatus.fail.push(...status.fail); if (!remainingNodes.length) {
this.restoreProcessStatus.success.push(...status.success); this.notification();
}), } else {
mergeMap(() => this.getDeletedNodes()) this.recover(remainingNodes);
) }
.subscribe((deletedNodesList) => { });
const { entries: nodeList } = deletedNodesList.list;
const { fail: restoreErrorNodes } = this.restoreProcessStatus;
const selectedNodes = this.diff(restoreErrorNodes, selection, false);
const remainingNodes = this.diff(selectedNodes, nodeList);
if (!remainingNodes.length) {
this.notification();
} else {
this.recover(remainingNodes);
}
});
} else { } else {
this.restoreProcessStatus.fail.push(...selection); this.restoreProcessStatus.fail.push(...selection);
this.notification(); this.notification();
@ -117,7 +116,7 @@ export class NodeRestoreDirective {
entry entry
})), })),
catchError((error) => { catchError((error) => {
const { statusCode } = (JSON.parse(error.message)).error; const { statusCode } = JSON.parse(error.message).error;
return of({ return of({
status: 0, status: 0,
@ -145,10 +144,10 @@ export class NodeRestoreDirective {
fail: [], fail: [],
success: [], success: [],
get someFailed() { get someFailed() {
return !!(this.fail.length); return !!this.fail.length;
}, },
get someSucceeded() { get someSucceeded() {
return !!(this.success.length); return !!this.success.length;
}, },
get oneFailed() { get oneFailed() {
return this.fail.length === 1; return this.fail.length === 1;
@ -168,58 +167,43 @@ export class NodeRestoreDirective {
} }
}; };
return data.reduce( return data.reduce((acc, node) => {
(acc, node) => { if (node.status) {
if (node.status) { acc.success.push(node);
acc.success.push(node); } else {
} else { acc.fail.push(node);
acc.fail.push(node); }
}
return acc; return acc;
}, }, status);
status
);
} }
private getRestoreMessage(): string | null { private getRestoreMessage(): string | null {
const { restoreProcessStatus: status } = this; const { restoreProcessStatus: status } = this;
if (status.someFailed && !status.oneFailed) { if (status.someFailed && !status.oneFailed) {
return this.translation.instant( return this.translation.instant('CORE.RESTORE_NODE.PARTIAL_PLURAL', {
'CORE.RESTORE_NODE.PARTIAL_PLURAL', // eslint-disable-next-line id-blacklist
{ number: status.fail.length
// eslint-disable-next-line id-blacklist });
number: status.fail.length
}
);
} }
if (status.oneFailed && status.fail[0].statusCode) { if (status.oneFailed && status.fail[0].statusCode) {
if (status.fail[0].statusCode === 409) { if (status.fail[0].statusCode === 409) {
return this.translation.instant( return this.translation.instant('CORE.RESTORE_NODE.NODE_EXISTS', {
'CORE.RESTORE_NODE.NODE_EXISTS', name: status.fail[0].entry.name
{ });
name: status.fail[0].entry.name
}
);
} else { } else {
return this.translation.instant( return this.translation.instant('CORE.RESTORE_NODE.GENERIC', {
'CORE.RESTORE_NODE.GENERIC', name: status.fail[0].entry.name
{ });
name: status.fail[0].entry.name
}
);
} }
} }
if (status.oneFailed && !status.fail[0].statusCode) { if (status.oneFailed && !status.fail[0].statusCode) {
return this.translation.instant( return this.translation.instant('CORE.RESTORE_NODE.LOCATION_MISSING', {
'CORE.RESTORE_NODE.LOCATION_MISSING', name: status.fail[0].entry.name
{ });
name: status.fail[0].entry.name
}
);
} }
if (status.allSucceeded && !status.oneSucceeded) { if (status.allSucceeded && !status.oneSucceeded) {
@ -227,12 +211,9 @@ export class NodeRestoreDirective {
} }
if (status.allSucceeded && status.oneSucceeded) { if (status.allSucceeded && status.oneSucceeded) {
return this.translation.instant( return this.translation.instant('CORE.RESTORE_NODE.SINGULAR', {
'CORE.RESTORE_NODE.SINGULAR', name: status.success[0].entry.name
{ });
name: status.success[0].entry.name
}
);
} }
return null; return null;
@ -244,7 +225,7 @@ export class NodeRestoreDirective {
const message = this.getRestoreMessage(); const message = this.getRestoreMessage();
this.reset(); this.reset();
const action = (status.oneSucceeded && !status.someFailed) ? this.translation.instant('CORE.RESTORE_NODE.VIEW') : ''; const action = status.oneSucceeded && !status.someFailed ? this.translation.instant('CORE.RESTORE_NODE.VIEW') : '';
let path; let path;
if (status.success && status.success.length > 0) { if (status.success && status.success.length > 0) {

View File

@ -15,37 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { MaterialModule } from '../material.module';
import { A11yModule } from '@angular/cdk/a11y';
import { AlfrescoViewerComponent } from './components/alfresco-viewer.component'; import { AlfrescoViewerComponent } from './components/alfresco-viewer.component';
import { CoreModule } from '@alfresco/adf-core';
import { ContentDirectiveModule } from '../directives';
/** @deprecated import AlfrescoViewerComponent instead */
@NgModule({ @NgModule({
imports: [ imports: [AlfrescoViewerComponent],
CoreModule, exports: [AlfrescoViewerComponent]
CommonModule,
MaterialModule,
TranslateModule,
FormsModule,
ReactiveFormsModule,
A11yModule,
ExtensionsModule,
ContentDirectiveModule
],
declarations: [
AlfrescoViewerComponent
],
exports: [
AlfrescoViewerComponent
]
}) })
export class AlfrescoViewerModule { export class AlfrescoViewerModule {}
}

View File

@ -34,6 +34,7 @@ import {
CloseButtonPosition, CloseButtonPosition,
Track, Track,
ViewerComponent, ViewerComponent,
ViewerModule,
ViewerMoreActionsComponent, ViewerMoreActionsComponent,
ViewerOpenWithComponent, ViewerOpenWithComponent,
ViewerSidebarComponent, ViewerSidebarComponent,
@ -51,9 +52,16 @@ import { NodesApiService } from '../../common/services/nodes-api.service';
import { UploadService } from '../../common/services/upload.service'; import { UploadService } from '../../common/services/upload.service';
import { FileModel } from '../../common/models/file.model'; import { FileModel } from '../../common/models/file.model';
import { NodeActionsService } from '../../document-list'; import { NodeActionsService } from '../../document-list';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { NodeDownloadDirective } from '../../directives';
@Component({ @Component({
selector: 'adf-alfresco-viewer', selector: 'adf-alfresco-viewer',
standalone: true,
imports: [CommonModule, TranslateModule, MatButtonModule, MatIconModule, ViewerModule, NodeDownloadDirective],
templateUrl: './alfresco-viewer.component.html', templateUrl: './alfresco-viewer.component.html',
styleUrls: ['./alfresco-viewer.component.scss'], styleUrls: ['./alfresco-viewer.component.scss'],
host: { class: 'adf-alfresco-viewer' }, host: { class: 'adf-alfresco-viewer' },
@ -115,7 +123,7 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
/** /**
* If `true` then show the Viewer as a full page over the current content. * If `true` then show the Viewer as a full page over the current content.
* Otherwise fit inside the parent div. * Otherwise, fit inside the parent div.
*/ */
@Input() @Input()
overlayMode = false; overlayMode = false;
@ -211,25 +219,25 @@ export class AlfrescoViewerComponent implements OnChanges, OnInit, OnDestroy {
sidebarRightTemplateContext: { node: Node } = { node: null }; sidebarRightTemplateContext: { node: Node } = { node: null };
sidebarLeftTemplateContext: { node: Node } = { node: null }; sidebarLeftTemplateContext: { node: Node } = { node: null };
_sharedLinksApi: SharedlinksApi; private _sharedLinksApi: SharedlinksApi;
get sharedLinksApi(): SharedlinksApi { get sharedLinksApi(): SharedlinksApi {
this._sharedLinksApi = this._sharedLinksApi ?? new SharedlinksApi(this.apiService.getInstance()); this._sharedLinksApi = this._sharedLinksApi ?? new SharedlinksApi(this.apiService.getInstance());
return this._sharedLinksApi; return this._sharedLinksApi;
} }
_versionsApi: VersionsApi; private _versionsApi: VersionsApi;
get versionsApi(): VersionsApi { get versionsApi(): VersionsApi {
this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance()); this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance());
return this._versionsApi; return this._versionsApi;
} }
_nodesApi: NodesApi; private _nodesApi: NodesApi;
get nodesApi(): NodesApi { get nodesApi(): NodesApi {
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance()); this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
return this._nodesApi; return this._nodesApi;
} }
_contentApi: ContentApi; private _contentApi: ContentApi;
get contentApi(): ContentApi { get contentApi(): ContentApi {
this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance()); this._contentApi = this._contentApi ?? new ContentApi(this.apiService.getInstance());
return this._contentApi; return this._contentApi;

View File

@ -29,154 +29,154 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
class MockRouter { class MockRouter {
private url = 'some-url'; private url = 'some-url';
private subject = new Subject(); private subject = new Subject();
events = this.subject.asObservable(); events = this.subject.asObservable();
routerState = { snapshot: { url: this.url } }; routerState = { snapshot: { url: this.url } };
navigateByUrl(url: string) { navigateByUrl(url: string) {
const navigationStart = new NavigationStart(0, url); const navigationStart = new NavigationStart(0, url);
this.subject.next(navigationStart); this.subject.next(navigationStart);
} }
} }
describe('AppLayoutComponent', () => { describe('AppLayoutComponent', () => {
let fixture: ComponentFixture<ShellLayoutComponent>; let fixture: ComponentFixture<ShellLayoutComponent>;
let component: ShellLayoutComponent; let component: ShellLayoutComponent;
let appConfig: AppConfigService; let appConfig: AppConfigService;
let shellAppService: ShellAppService; let shellAppService: ShellAppService;
beforeEach(() => { beforeEach(() => {
const shellService: ShellAppService = { const shellService: ShellAppService = {
pageHeading$: of('Title'), pageHeading$: of('Title'),
hideSidenavConditions: [], hideSidenavConditions: [],
minimizeSidenavConditions: [], minimizeSidenavConditions: [],
preferencesService: { preferencesService: {
get: () => 'true', get: () => 'true',
set: () => {} set: () => {}
} }
}; };
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CommonModule, CommonModule,
NoopAnimationsModule, NoopAnimationsModule,
HttpClientModule, HttpClientModule,
SidenavLayoutModule, SidenavLayoutModule,
ExtensionsModule, ExtensionsModule,
RouterModule.forChild([]), RouterModule.forChild([]),
TranslateModule.forRoot() TranslateModule.forRoot(),
], ShellLayoutComponent
providers: [ ],
{ providers: [
provide: Router, {
useClass: MockRouter provide: Router,
}, useClass: MockRouter
{ },
provide: SHELL_APP_SERVICE, {
useValue: shellService provide: SHELL_APP_SERVICE,
} useValue: shellService
], }
declarations: [ShellLayoutComponent], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
});
fixture = TestBed.createComponent(ShellLayoutComponent);
component = fixture.componentInstance;
appConfig = TestBed.inject(AppConfigService);
shellAppService = TestBed.inject(SHELL_APP_SERVICE);
}); });
fixture = TestBed.createComponent(ShellLayoutComponent); beforeEach(() => {
component = fixture.componentInstance; appConfig.config.languages = [];
appConfig = TestBed.inject(AppConfigService); appConfig.config.locale = 'en';
shellAppService = TestBed.inject(SHELL_APP_SERVICE);
});
beforeEach(() => {
appConfig.config.languages = [];
appConfig.config.locale = 'en';
});
describe('sidenav state', () => {
it('should get state from configuration', () => {
appConfig.config.sideNav = {
expandedSidenav: false,
preserveState: false
};
fixture.detectChanges();
expect(component.expandedSidenav).toBe(false);
}); });
it('should resolve state to true is no configuration', () => { describe('sidenav state', () => {
appConfig.config.sidenav = {}; it('should get state from configuration', () => {
appConfig.config.sideNav = {
expandedSidenav: false,
preserveState: false
};
fixture.detectChanges(); fixture.detectChanges();
expect(component.expandedSidenav).toBe(true); expect(component.expandedSidenav).toBe(false);
});
it('should resolve state to true is no configuration', () => {
appConfig.config.sidenav = {};
fixture.detectChanges();
expect(component.expandedSidenav).toBe(true);
});
it('should get state from user settings as true', () => {
appConfig.config.sideNav = {
expandedSidenav: false,
preserveState: true
};
spyOn(shellAppService.preferencesService, 'get').and.callFake((key) => {
if (key === 'expandedSidenav') {
return 'true';
}
return 'false';
});
fixture.detectChanges();
expect(component.expandedSidenav).toBe(true);
});
it('should get state from user settings as false', () => {
appConfig.config.sidenav = {
expandedSidenav: false,
preserveState: true
};
spyOn(shellAppService.preferencesService, 'get').and.callFake((key) => {
if (key === 'expandedSidenav') {
return 'false';
}
return 'true';
});
fixture.detectChanges();
expect(component.expandedSidenav).toBe(false);
});
}); });
it('should get state from user settings as true', () => { it('should close menu on mobile screen size', () => {
appConfig.config.sideNav = { component.minimizeSidenav = false;
expandedSidenav: false, component.layout.container = {
preserveState: true isMobileScreenSize: true,
}; toggleMenu: () => {}
};
spyOn(shellAppService.preferencesService, 'get').and.callFake((key) => { spyOn(component.layout.container, 'toggleMenu');
if (key === 'expandedSidenav') { fixture.detectChanges();
return 'true';
}
return 'false';
});
fixture.detectChanges(); component.hideMenu({ preventDefault: () => {} } as any);
expect(component.expandedSidenav).toBe(true); expect(component.layout.container.toggleMenu).toHaveBeenCalled();
}); });
it('should get state from user settings as false', () => { it('should close menu on mobile screen size also when minimizeSidenav true', () => {
appConfig.config.sidenav = { fixture.detectChanges();
expandedSidenav: false, component.minimizeSidenav = true;
preserveState: true component.layout.container = {
}; isMobileScreenSize: true,
toggleMenu: () => {}
};
spyOn(shellAppService.preferencesService, 'get').and.callFake((key) => { spyOn(component.layout.container, 'toggleMenu');
if (key === 'expandedSidenav') { fixture.detectChanges();
return 'false';
}
return 'true';
});
fixture.detectChanges(); component.hideMenu({ preventDefault: () => {} } as any);
expect(component.expandedSidenav).toBe(false); expect(component.layout.container.toggleMenu).toHaveBeenCalled();
}); });
});
it('should close menu on mobile screen size', () => {
component.minimizeSidenav = false;
component.layout.container = {
isMobileScreenSize: true,
toggleMenu: () => {}
};
spyOn(component.layout.container, 'toggleMenu');
fixture.detectChanges();
component.hideMenu({ preventDefault: () => {} } as any);
expect(component.layout.container.toggleMenu).toHaveBeenCalled();
});
it('should close menu on mobile screen size also when minimizeSidenav true', () => {
fixture.detectChanges();
component.minimizeSidenav = true;
component.layout.container = {
isMobileScreenSize: true,
toggleMenu: () => {}
};
spyOn(component.layout.container, 'toggleMenu');
fixture.detectChanges();
component.hideMenu({ preventDefault: () => {} } as any);
expect(component.layout.container.toggleMenu).toHaveBeenCalled();
});
}); });

View File

@ -15,125 +15,129 @@
* limitations under the License. * limitations under the License.
*/ */
import { AppConfigService, SidenavLayoutComponent } from '@alfresco/adf-core'; import { AppConfigService, SidenavLayoutComponent, SidenavLayoutModule } from '@alfresco/adf-core';
import { DynamicExtensionComponent } from '@alfresco/adf-extensions';
import { Component, Inject, OnDestroy, OnInit, Optional, ViewChild, ViewEncapsulation } from '@angular/core'; import { Component, Inject, OnDestroy, OnInit, Optional, ViewChild, ViewEncapsulation } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router, RouterModule } from '@angular/router';
import { Subject, Observable } from 'rxjs'; import { Subject, Observable } from 'rxjs';
import { filter, takeUntil, map, withLatestFrom } from 'rxjs/operators'; import { filter, takeUntil, map, withLatestFrom } from 'rxjs/operators';
import { BreakpointObserver } from '@angular/cdk/layout'; import { BreakpointObserver } from '@angular/cdk/layout';
import { Directionality } from '@angular/cdk/bidi'; import { Directionality } from '@angular/cdk/bidi';
import { SHELL_APP_SERVICE, ShellAppService, SHELL_NAVBAR_MIN_WIDTH, SHELL_NAVBAR_MAX_WIDTH } from '../../services/shell-app.service'; import { SHELL_APP_SERVICE, ShellAppService, SHELL_NAVBAR_MIN_WIDTH, SHELL_NAVBAR_MAX_WIDTH } from '../../services/shell-app.service';
import { CommonModule } from '@angular/common';
@Component({ @Component({
selector: 'app-shell', selector: 'app-shell',
templateUrl: './shell.component.html', standalone: true,
styleUrls: ['./shell.component.scss'], imports: [CommonModule, SidenavLayoutModule, RouterModule, DynamicExtensionComponent],
encapsulation: ViewEncapsulation.None, templateUrl: './shell.component.html',
host: { class: 'app-shell' } styleUrls: ['./shell.component.scss'],
encapsulation: ViewEncapsulation.None,
host: { class: 'app-shell' }
}) })
export class ShellLayoutComponent implements OnInit, OnDestroy { export class ShellLayoutComponent implements OnInit, OnDestroy {
@ViewChild('layout', { static: true }) @ViewChild('layout', { static: true })
layout: SidenavLayoutComponent; layout: SidenavLayoutComponent;
onDestroy$: Subject<boolean> = new Subject<boolean>(); onDestroy$: Subject<boolean> = new Subject<boolean>();
isSmallScreen$: Observable<boolean>; isSmallScreen$: Observable<boolean>;
expandedSidenav: boolean; expandedSidenav: boolean;
minimizeSidenav = false; minimizeSidenav = false;
hideSidenav = false; hideSidenav = false;
sidenavMin: number; sidenavMin: number;
sidenavMax: number; sidenavMax: number;
direction: Directionality; direction: Directionality;
constructor( constructor(
private router: Router, private router: Router,
private appConfigService: AppConfigService, private appConfigService: AppConfigService,
private breakpointObserver: BreakpointObserver, private breakpointObserver: BreakpointObserver,
@Inject(SHELL_APP_SERVICE) private shellService: ShellAppService, @Inject(SHELL_APP_SERVICE) private shellService: ShellAppService,
@Optional() @Inject(SHELL_NAVBAR_MIN_WIDTH) navBarMinWidth: number, @Optional() @Inject(SHELL_NAVBAR_MIN_WIDTH) navBarMinWidth: number,
@Optional() @Inject(SHELL_NAVBAR_MAX_WIDTH) navbarMaxWidth: number @Optional() @Inject(SHELL_NAVBAR_MAX_WIDTH) navbarMaxWidth: number
) { ) {
this.sidenavMin = navBarMinWidth ?? 70; this.sidenavMin = navBarMinWidth ?? 70;
this.sidenavMax = navbarMaxWidth ?? 320; this.sidenavMax = navbarMaxWidth ?? 320;
}
ngOnInit() {
this.isSmallScreen$ = this.breakpointObserver.observe(['(max-width: 600px)']).pipe(map((result) => result.matches));
this.hideSidenav = this.shellService.hideSidenavConditions.some((el) => this.router.routerState.snapshot.url.includes(el));
this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => this.router.routerState.snapshot.url.includes(el));
if (!this.minimizeSidenav) {
this.expandedSidenav = this.getSidenavState();
} else {
this.expandedSidenav = false;
} }
this.router.events ngOnInit() {
.pipe( this.isSmallScreen$ = this.breakpointObserver.observe(['(max-width: 600px)']).pipe(map((result) => result.matches));
withLatestFrom(this.isSmallScreen$),
filter(([event, isSmallScreen]) => isSmallScreen && event instanceof NavigationEnd),
takeUntil(this.onDestroy$)
)
.subscribe(() => {
this.layout.container.sidenav.close();
});
this.router.events this.hideSidenav = this.shellService.hideSidenavConditions.some((el) => this.router.routerState.snapshot.url.includes(el));
.pipe( this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => this.router.routerState.snapshot.url.includes(el));
filter((event) => event instanceof NavigationEnd),
takeUntil(this.onDestroy$)
)
.subscribe((event: NavigationEnd) => {
this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
this.hideSidenav = this.shellService.hideSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
this.updateState(); if (!this.minimizeSidenav) {
}); this.expandedSidenav = this.getSidenavState();
} } else {
this.expandedSidenav = false;
}
ngOnDestroy() { this.router.events
this.onDestroy$.next(true); .pipe(
this.onDestroy$.complete(); withLatestFrom(this.isSmallScreen$),
} filter(([event, isSmallScreen]) => isSmallScreen && event instanceof NavigationEnd),
takeUntil(this.onDestroy$)
)
.subscribe(() => {
this.layout.container.sidenav.close();
});
hideMenu(event: Event) { this.router.events
if (this.layout.container.isMobileScreenSize) { .pipe(
event.preventDefault(); filter((event) => event instanceof NavigationEnd),
this.layout.container.toggleMenu(); takeUntil(this.onDestroy$)
} )
} .subscribe((event: NavigationEnd) => {
this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
this.hideSidenav = this.shellService.hideSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
private updateState() { this.updateState();
if (this.minimizeSidenav && !this.layout.isMenuMinimized) { });
this.layout.isMenuMinimized = true;
if (!this.layout.container.isMobileScreenSize) {
this.layout.container.toggleMenu();
}
} }
if (!this.minimizeSidenav) { ngOnDestroy() {
if (this.getSidenavState() && this.layout.isMenuMinimized) { this.onDestroy$.next(true);
this.layout.isMenuMinimized = false; this.onDestroy$.complete();
this.layout.container.toggleMenu();
}
}
}
onExpanded(state: boolean) {
if (!this.minimizeSidenav && this.appConfigService.get('sideNav.preserveState')) {
this.shellService.preferencesService.set('expandedSidenav', state);
}
}
private getSidenavState(): boolean {
const expand = this.appConfigService.get<boolean>('sideNav.expandedSidenav', true);
const preserveState = this.appConfigService.get<boolean>('sideNav.preserveState', true);
if (preserveState) {
return this.shellService.preferencesService.get('expandedSidenav', expand.toString()) === 'true';
} }
return expand; hideMenu(event: Event) {
} if (this.layout.container.isMobileScreenSize) {
event.preventDefault();
this.layout.container.toggleMenu();
}
}
private updateState() {
if (this.minimizeSidenav && !this.layout.isMenuMinimized) {
this.layout.isMenuMinimized = true;
if (!this.layout.container.isMobileScreenSize) {
this.layout.container.toggleMenu();
}
}
if (!this.minimizeSidenav) {
if (this.getSidenavState() && this.layout.isMenuMinimized) {
this.layout.isMenuMinimized = false;
this.layout.container.toggleMenu();
}
}
}
onExpanded(state: boolean) {
if (!this.minimizeSidenav && this.appConfigService.get('sideNav.preserveState')) {
this.shellService.preferencesService.set('expandedSidenav', state);
}
}
private getSidenavState(): boolean {
const expand = this.appConfigService.get<boolean>('sideNav.expandedSidenav', true);
const preserveState = this.appConfigService.get<boolean>('sideNav.preserveState', true);
if (preserveState) {
return this.shellService.preferencesService.get('expandedSidenav', expand.toString()) === 'true';
}
return expand;
}
} }

View File

@ -15,32 +15,28 @@
* limitations under the License. * limitations under the License.
*/ */
import { CommonModule } from '@angular/common';
import { ModuleWithProviders, NgModule } from '@angular/core'; import { ModuleWithProviders, NgModule } from '@angular/core';
import { Routes, provideRoutes, RouterModule, Route } from '@angular/router'; import { Routes, provideRoutes, Route } from '@angular/router';
import { SHELL_LAYOUT_ROUTE } from './shell.routes'; import { SHELL_LAYOUT_ROUTE } from './shell.routes';
import { SidenavLayoutModule } from '@alfresco/adf-core';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { ShellLayoutComponent } from './components/shell/shell.component'; import { ShellLayoutComponent } from './components/shell/shell.component';
export interface AppShellRoutesConfig { export interface AppShellRoutesConfig {
shellParentRoute?: Route; shellParentRoute?: Route;
shellChildren: Routes; shellChildren: Routes;
} }
@NgModule({ @NgModule({
imports: [SidenavLayoutModule, ExtensionsModule, RouterModule.forChild([]), CommonModule], imports: [ShellLayoutComponent],
exports: [ShellLayoutComponent], exports: [ShellLayoutComponent]
declarations: [ShellLayoutComponent]
}) })
export class ShellModule { export class ShellModule {
static withRoutes(routes: Routes | AppShellRoutesConfig): ModuleWithProviders<ShellModule> { static withRoutes(routes: Routes | AppShellRoutesConfig): ModuleWithProviders<ShellModule> {
if (Array.isArray(routes)) { if (Array.isArray(routes)) {
return getModuleForRoutes(routes); return getModuleForRoutes(routes);
} }
return getModuleForRouteConfig(routes); return getModuleForRouteConfig(routes);
} }
} }
/** /**
@ -50,16 +46,16 @@ export class ShellModule {
* @returns module with providers * @returns module with providers
*/ */
function getModuleForRoutes(routes: Routes): ModuleWithProviders<ShellModule> { function getModuleForRoutes(routes: Routes): ModuleWithProviders<ShellModule> {
const shellLayoutRoute = SHELL_LAYOUT_ROUTE; const shellLayoutRoute = SHELL_LAYOUT_ROUTE;
routes.forEach((childRoute) => { routes.forEach((childRoute) => {
shellLayoutRoute.children.push(childRoute); shellLayoutRoute.children.push(childRoute);
}); });
return { return {
ngModule: ShellModule, ngModule: ShellModule,
providers: provideRoutes([shellLayoutRoute]) providers: provideRoutes([shellLayoutRoute])
}; };
} }
/** /**
@ -69,25 +65,25 @@ function getModuleForRoutes(routes: Routes): ModuleWithProviders<ShellModule> {
* @returns module with providers * @returns module with providers
*/ */
function getModuleForRouteConfig(config: AppShellRoutesConfig): ModuleWithProviders<ShellModule> { function getModuleForRouteConfig(config: AppShellRoutesConfig): ModuleWithProviders<ShellModule> {
const shellLayoutRoute = SHELL_LAYOUT_ROUTE; const shellLayoutRoute = SHELL_LAYOUT_ROUTE;
const shellParentRoute = config.shellParentRoute; const shellParentRoute = config.shellParentRoute;
const shellChildrenRoutes = config.shellChildren; const shellChildrenRoutes = config.shellChildren;
shellLayoutRoute.children.push(...shellChildrenRoutes); shellLayoutRoute.children.push(...shellChildrenRoutes);
const rootRoute = shellParentRoute ? shellParentRoute : shellLayoutRoute; const rootRoute = shellParentRoute ? shellParentRoute : shellLayoutRoute;
if (config.shellParentRoute) { if (config.shellParentRoute) {
if (rootRoute.children === undefined) { if (rootRoute.children === undefined) {
rootRoute.children = []; rootRoute.children = [];
}
rootRoute.children.push(shellLayoutRoute);
} }
rootRoute.children.push(shellLayoutRoute); return {
} ngModule: ShellModule,
providers: provideRoutes([rootRoute])
return { };
ngModule: ShellModule,
providers: provideRoutes([rootRoute])
};
} }

View File

@ -20,10 +20,28 @@ import { AboutComponent } from './about.component';
import { AboutModule } from './about.module'; import { AboutModule } from './about.module';
import { AuthenticationService } from '../auth/services/authentication.service'; import { AuthenticationService } from '../auth/services/authentication.service';
import { AuthenticationMock } from '../auth/mock/authentication.service.mock'; import { AuthenticationMock } from '../auth/mock/authentication.service.mock';
import { AppExtensionService, AppExtensionServiceMock } from '@alfresco/adf-extensions'; import { AppExtensionService, ExtensionRef, ViewerExtensionRef } from '@alfresco/adf-extensions';
import { AppConfigService } from '../app-config/app-config.service'; import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigServiceMock } from '../common/mock/app-config.service.mock'; import { AppConfigServiceMock } from '../common/mock/app-config.service.mock';
import { CoreStoryModule } from '../testing/core.story.module'; import { CoreStoryModule } from '../testing/core.story.module';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AppExtensionServiceMock {
references$: Observable<ExtensionRef[]>;
private _references = new BehaviorSubject<ExtensionRef[]>([]);
constructor() {
this.references$ = this._references.asObservable();
}
getViewerExtensions(): ViewerExtensionRef[] {
return [];
}
}
export default { export default {
component: AboutComponent, component: AboutComponent,
@ -74,7 +92,10 @@ const template: Story<AboutComponent> = (args: AboutComponent) => ({
export const about = template.bind({}); export const about = template.bind({});
about.args = { about.args = {
pkg: { pkg: {
name: 'My Storybook App', commit: 'my-commit-value', version: '1.0.0', dependencies: { name: 'My Storybook App',
commit: 'my-commit-value',
version: '1.0.0',
dependencies: {
'@alfresco/adf-content-services': '4.7.0', '@alfresco/adf-content-services': '4.7.0',
'@alfresco/adf-core': '4.7.0', '@alfresco/adf-core': '4.7.0',
'@alfresco/adf-extensions': '4.7.0', '@alfresco/adf-extensions': '4.7.0',

View File

@ -15,7 +15,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { A11yModule } from '@angular/cdk/a11y'; import { A11yModule } from '@angular/cdk/a11y';
import { NgIf, NgTemplateOutlet } from '@angular/common'; import { NgIf, NgTemplateOutlet } from '@angular/common';
import { import {
@ -41,7 +40,6 @@ import { TranslateModule } from '@ngx-translate/core';
import { fromEvent, Subject } from 'rxjs'; import { fromEvent, Subject } from 'rxjs';
import { filter, first, skipWhile, takeUntil } from 'rxjs/operators'; import { filter, first, skipWhile, takeUntil } from 'rxjs/operators';
import { AppConfigService } from '../../app-config'; import { AppConfigService } from '../../app-config';
import { PipeModule } from '../../pipes';
import { ToolbarComponent, ToolbarDividerComponent, ToolbarTitleComponent } from '../../toolbar'; import { ToolbarComponent, ToolbarDividerComponent, ToolbarTitleComponent } from '../../toolbar';
import { DownloadPromptActions } from '../models/download-prompt.actions'; import { DownloadPromptActions } from '../models/download-prompt.actions';
import { CloseButtonPosition, Track } from '../models/viewer.model'; import { CloseButtonPosition, Track } from '../models/viewer.model';
@ -52,6 +50,9 @@ import { ViewerOpenWithComponent } from './viewer-open-with.component';
import { ViewerRenderComponent } from './viewer-render/viewer-render.component'; import { ViewerRenderComponent } from './viewer-render/viewer-render.component';
import { ViewerSidebarComponent } from './viewer-sidebar.component'; import { ViewerSidebarComponent } from './viewer-sidebar.component';
import { ViewerToolbarComponent } from './viewer-toolbar.component'; import { ViewerToolbarComponent } from './viewer-toolbar.component';
import { ViewerToolbarActionsComponent } from './viewer-toolbar-actions.component';
import { ViewerToolbarCustomActionsComponent } from './viewer-toolbar-custom-actions.component';
import { MimeTypeIconPipe } from '../../pipes';
const DEFAULT_NON_PREVIEW_CONFIG = { const DEFAULT_NON_PREVIEW_CONFIG = {
enableDownloadPrompt: false, enableDownloadPrompt: false,
@ -75,12 +76,15 @@ const DEFAULT_NON_PREVIEW_CONFIG = {
MatButtonModule, MatButtonModule,
TranslateModule, TranslateModule,
MatIconModule, MatIconModule,
PipeModule,
MatMenuModule, MatMenuModule,
ToolbarDividerComponent, ToolbarDividerComponent,
ViewerRenderComponent, ViewerRenderComponent,
NgTemplateOutlet, NgTemplateOutlet,
ExtensionsModule ViewerToolbarComponent,
ViewerSidebarComponent,
ViewerToolbarActionsComponent,
ViewerToolbarCustomActionsComponent,
MimeTypeIconPipe
], ],
providers: [ViewUtilService] providers: [ViewUtilService]
}) })
@ -137,7 +141,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
/** /**
* If `true` then show the Viewer as a full page over the current content. * If `true` then show the Viewer as a full page over the current content.
* Otherwise fit inside the parent div. * Otherwise, fit inside the parent div.
*/ */
@Input() @Input()
overlayMode = false; overlayMode = false;

View File

@ -35,45 +35,29 @@ import { ViewerToolbarComponent } from './components/viewer-toolbar.component';
import { ViewerComponent } from './components/viewer.component'; import { ViewerComponent } from './components/viewer.component';
import { ViewerExtensionDirective } from './directives/viewer-extension.directive'; import { ViewerExtensionDirective } from './directives/viewer-extension.directive';
export const VIEWER_DIRECTIVES = [
PdfPasswordDialogComponent,
ViewerRenderComponent,
ImgViewerComponent,
TxtViewerComponent,
MediaPlayerComponent,
PdfViewerComponent,
PdfThumbComponent,
PdfThumbListComponent,
ViewerExtensionDirective,
UnknownFormatComponent,
ViewerToolbarComponent,
ViewerSidebarComponent,
ViewerOpenWithComponent,
ViewerMoreActionsComponent,
ViewerToolbarActionsComponent,
ViewerComponent,
ViewerToolbarCustomActionsComponent,
DownloadPromptDialogComponent
] as const;
@NgModule({ @NgModule({
imports: [ imports: [...VIEWER_DIRECTIVES],
PdfPasswordDialogComponent, exports: [...VIEWER_DIRECTIVES]
ViewerRenderComponent,
ImgViewerComponent,
TxtViewerComponent,
MediaPlayerComponent,
PdfViewerComponent,
PdfThumbComponent,
PdfThumbListComponent,
ViewerExtensionDirective,
UnknownFormatComponent,
ViewerToolbarComponent,
ViewerSidebarComponent,
ViewerOpenWithComponent,
ViewerMoreActionsComponent,
ViewerToolbarActionsComponent,
ViewerComponent,
ViewerToolbarCustomActionsComponent,
DownloadPromptDialogComponent
],
exports: [
ViewerRenderComponent,
ImgViewerComponent,
TxtViewerComponent,
MediaPlayerComponent,
PdfViewerComponent,
PdfPasswordDialogComponent,
PdfThumbComponent,
PdfThumbListComponent,
ViewerExtensionDirective,
UnknownFormatComponent,
ViewerToolbarComponent,
ViewerSidebarComponent,
ViewerOpenWithComponent,
ViewerMoreActionsComponent,
ViewerToolbarActionsComponent,
ViewerComponent,
ViewerToolbarCustomActionsComponent
]
}) })
export class ViewerModule {} export class ViewerModule {}

View File

@ -32,7 +32,8 @@ import { ExtensionService } from '../../services/extension.service';
@Component({ @Component({
selector: 'adf-dynamic-column', selector: 'adf-dynamic-column',
template: ` <ng-container #content></ng-container> `, standalone: true,
template: `<ng-container #content></ng-container>`,
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'adf-dynamic-column' }, host: { class: 'adf-dynamic-column' },

View File

@ -26,6 +26,7 @@ import { HttpClientModule } from '@angular/common/http';
@Component({ @Component({
selector: 'test-component', selector: 'test-component',
standalone: true,
template: '<div data-automation-id="found-me">Hey I am the mighty test component!</div>' template: '<div data-automation-id="found-me">Hey I am the mighty test component!</div>'
}) })
export class TestComponent implements OnChanges { export class TestComponent implements OnChanges {
@ -47,8 +48,7 @@ describe('DynamicExtensionComponent', () => {
componentRegister.setComponents({ 'test-component': TestComponent }); componentRegister.setComponents({ 'test-component': TestComponent });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientModule], imports: [HttpClientModule, DynamicExtensionComponent, TestComponent],
declarations: [DynamicExtensionComponent, TestComponent],
providers: [{ provide: ComponentRegisterService, useValue: componentRegister }] providers: [{ provide: ComponentRegisterService, useValue: componentRegister }]
}); });

View File

@ -22,6 +22,7 @@ import { ExtensionComponent } from '../../services/component-register.service';
// cSpell:words lifecycle // cSpell:words lifecycle
@Component({ @Component({
selector: 'adf-dynamic-component', selector: 'adf-dynamic-component',
standalone: true,
template: `<div #content></div>` template: `<div #content></div>`
}) })
export class DynamicExtensionComponent implements OnChanges, OnDestroy { export class DynamicExtensionComponent implements OnChanges, OnDestroy {

View File

@ -21,6 +21,7 @@ import { ExtensionService } from '../../services/extension.service';
@Component({ @Component({
selector: 'adf-dynamic-tab', selector: 'adf-dynamic-tab',
standalone: true,
template: `<div #content></div>` template: `<div #content></div>`
}) })
export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy { export class DynamicTabComponent implements OnInit, OnChanges, OnDestroy {

View File

@ -20,7 +20,8 @@ import { ExtensionService } from '../../services/extension.service';
@Component({ @Component({
selector: 'adf-preview-extension', selector: 'adf-preview-extension',
template: ` <div #content></div> ` standalone: true,
template: `<div #content></div>`
}) })
export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy { export class PreviewExtensionComponent implements OnInit, OnChanges, OnDestroy {
@ViewChild('content', { read: ViewContainerRef, static: true }) @ViewChild('content', { read: ViewContainerRef, static: true })

View File

@ -23,19 +23,12 @@ import { NgModule, ModuleWithProviders, APP_INITIALIZER } from '@angular/core';
import { AppExtensionService } from './services/app-extension.service'; import { AppExtensionService } from './services/app-extension.service';
import { setupExtensions } from './services/startup-extension-factory'; import { setupExtensions } from './services/startup-extension-factory';
export const EXTENSION_DIRECTIVES = [DynamicExtensionComponent, DynamicTabComponent, DynamicColumnComponent, PreviewExtensionComponent] as const;
/** @deprecated import EXTENSION_DIRECTIVES or standalone components instead */
@NgModule({ @NgModule({
declarations: [ imports: [...EXTENSION_DIRECTIVES],
DynamicExtensionComponent, exports: [...EXTENSION_DIRECTIVES]
DynamicTabComponent,
DynamicColumnComponent,
PreviewExtensionComponent
],
exports: [
DynamicExtensionComponent,
DynamicTabComponent,
DynamicColumnComponent,
PreviewExtensionComponent
]
}) })
export class ExtensionsModule { export class ExtensionsModule {
static forRoot(): ModuleWithProviders<ExtensionsModule> { static forRoot(): ModuleWithProviders<ExtensionsModule> {
@ -51,10 +44,4 @@ export class ExtensionsModule {
] ]
}; };
} }
static forChild(): ModuleWithProviders<ExtensionsModule> {
return {
ngModule: ExtensionsModule
};
}
} }

View File

@ -1,37 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 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 { ExtensionRef } from '../config/extension.config';
import { Observable, BehaviorSubject } from 'rxjs';
import { ViewerExtensionRef } from '../config/viewer.extensions';
@Injectable({
providedIn: 'root'
})
export class AppExtensionServiceMock {
references$: Observable<ExtensionRef[]>;
private _references = new BehaviorSubject<ExtensionRef[]>([]);
constructor() {
this.references$ = this._references.asObservable();
}
getViewerExtensions(): ViewerExtensionRef[] {
return [];
}
}

View File

@ -1,18 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 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 * from './app-extension.service.mock';

View File

@ -41,5 +41,3 @@ export * from './lib/store/states/repository.state';
export * from './lib/components/public-api'; export * from './lib/components/public-api';
export * from './lib/extensions.module'; export * from './lib/extensions.module';
export * from './lib/mock/public-api';