diff --git a/docs/README.md b/docs/README.md
index 5a34474b58..30e6b7d1c0 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -178,7 +178,6 @@ A collection of Angular components for generic use.
| [Full name pipe](core/pipes/full-name.pipe.md) | Joins the first and last name properties from a `UserLike` object into a single string. | [Source](../lib/core/src/lib/pipes/full-name.pipe.ts) |
| [Localized Date pipe](core/pipes/localized-date.pipe.md) | Converts a date to a given format and locale. | [Source](../lib/core/src/lib/pipes/localized-date.pipe.ts) |
| [Multi Value Pipe](core/pipes/multi-value.pipe.md) | Takes an array of strings and turns it into one string where items are separated by a separator. The default separator applied to the list is the comma , however, you can set your own separator in the params of the pipe. | [Source](../lib/core/src/lib/pipes/multi-value.pipe.ts) |
-| [Node Name Tooltip pipe](core/pipes/node-name-tooltip.pipe.md) | Formats the tooltip for a Node. | [Source](../lib/content-services/src/lib/pipes/node-name-tooltip.pipe.ts) |
| [Text Highlight pipe](core/pipes/text-highlight.pipe.md) | Adds highlighting to words or sections of text that match a search string. | [Source](../lib/core/src/lib/pipes/text-highlight.pipe.ts) |
| [Time Ago pipe](core/pipes/time-ago.pipe.md) | Converts a recent past date into a number of days ago. | [Source](../lib/core/src/lib/pipes/time-ago.pipe.ts) |
| [User Initial pipe](core/pipes/user-initial.pipe.md) | Takes the name fields of a `UserLike` object and extracts and formats the initials. | [Source](../lib/core/src/lib/pipes/user-initial.pipe.ts) |
diff --git a/docs/core/pipes/node-name-tooltip.pipe.md b/docs/core/pipes/node-name-tooltip.pipe.md
deleted file mode 100644
index f19d0c12d9..0000000000
--- a/docs/core/pipes/node-name-tooltip.pipe.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-Title: Node Name Tooltip pipe
-Added: v2.0.0
-Status: Active
----
-
-# [Node Name Tooltip pipe](../../../lib/content-services/src/lib/pipes/node-name-tooltip.pipe.ts "Defined in node-name-tooltip.pipe.ts")
-
-Formats the tooltip for a [`Node`](https://github.com/Alfresco/alfresco-js-api/blob/develop/src/api/content-rest-api/docs/Node.md).
-
-## Basic Usage
-
-
-
-```html
-
-
- {{ value }}
-
-
-```
-
-
-
-## Details
-
-The tooltip is formatted according to the following rules:
-
-- if the _title_ and _description_ are missing, then the tooltip shows the _name_;
-- if the _title_ is missing, then the tooltip shows the _name_ and _description_;
-- if the _description_ is missing, then the tooltip shows the _name_ and _title_;
-- if _name_ and _title_, _name_ and _description_, or _title_ and _description_ are the same, then only a single line is displayed.
diff --git a/docs/versionIndex.md b/docs/versionIndex.md
index 041ccda419..dd527b061c 100644
--- a/docs/versionIndex.md
+++ b/docs/versionIndex.md
@@ -605,7 +605,6 @@ backend services have been tested with each released version of ADF.
- [Logout directive](core/directives/logout.directive.md)
- [Node delete directive](content-services/directives/node-delete.directive.md)
- [Node favorite directive](content-services/directives/node-favorite.directive.md)
-- [Node name tooltip pipe](core/pipes/node-name-tooltip.pipe.md)
- [Node permission service](content-services/services/node-permission.service.md)
- [Node restore directive](content-services/directives/node-restore.directive.md)
- [Nodes api service](core/services/nodes-api.service.md)
diff --git a/lib/content-services/src/lib/breadcrumb/dropdown-breadcrumb.component.ts b/lib/content-services/src/lib/breadcrumb/dropdown-breadcrumb.component.ts
index 3af9adc3bb..8046cb139c 100644
--- a/lib/content-services/src/lib/breadcrumb/dropdown-breadcrumb.component.ts
+++ b/lib/content-services/src/lib/breadcrumb/dropdown-breadcrumb.component.ts
@@ -33,10 +33,10 @@ import { IconModule } from '@alfresco/adf-core';
})
export class DropdownBreadcrumbComponent extends BreadcrumbComponent implements OnChanges {
@ViewChild('dropdown')
- dropdown: MatSelect;
+ declare dropdown: MatSelect;
currentNode: PathElement;
- previousNodes: PathElement[];
+ declare previousNodes: PathElement[];
/**
* Calculate the current and previous nodes from the route array
diff --git a/lib/content-services/src/lib/content.module.ts b/lib/content-services/src/lib/content.module.ts
index 84df9a9518..fcf5b3b2ee 100644
--- a/lib/content-services/src/lib/content.module.ts
+++ b/lib/content-services/src/lib/content.module.ts
@@ -32,7 +32,6 @@ import { CONTENT_PERMISSION_MANAGER_DIRECTIVES } from './permission-manager/perm
import { ASPECT_LIST_DIRECTIVES } from './aspect-list/aspect-list.module';
import { versionCompatibilityFactory } from './version-compatibility/version-compatibility-factory';
import { VersionCompatibilityService } from './version-compatibility/version-compatibility.service';
-import { CONTENT_PIPES } from './pipes/content-pipe.module';
import { contentAuthLoaderFactory } from './auth-loader/content-auth-loader-factory';
import { ContentAuthLoaderService } from './auth-loader/content-auth-loader.service';
import { CategoriesManagementComponent } from './category';
@@ -55,7 +54,6 @@ import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-facto
MaterialModule,
MatDatetimepickerModule,
MatNativeDatetimeModule,
- ...CONTENT_PIPES,
...CONTENT_TAG_DIRECTIVES,
...CONTENT_DIALOG_DIRECTIVES,
...CONTENT_SEARCH_DIRECTIVES,
@@ -82,7 +80,6 @@ import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-facto
providers: [provideTranslations('adf-content-services', 'assets/adf-content-services')],
exports: [
MaterialModule,
- ...CONTENT_PIPES,
...CONTENT_TAG_DIRECTIVES,
...DOCUMENT_LIST_DIRECTIVES,
...CONTENT_UPLOAD_DIRECTIVES,
diff --git a/lib/content-services/src/lib/document-list/components/document-list.component.ts b/lib/content-services/src/lib/document-list/components/document-list.component.ts
index 3b9415e007..ae83800fc8 100644
--- a/lib/content-services/src/lib/document-list/components/document-list.component.ts
+++ b/lib/content-services/src/lib/document-list/components/document-list.component.ts
@@ -125,7 +125,7 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
DEFAULT_SORTING: DataSorting[] = [new DataSorting('name', 'asc'), new DataSorting('isFolder', 'desc')];
@ContentChild(DataColumnListComponent)
- columnList: DataColumnListComponent;
+ declare columnList: DataColumnListComponent;
@ContentChild(CustomLoadingContentTemplateDirective)
customLoadingContent: CustomLoadingContentTemplateDirective;
diff --git a/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.spec.ts b/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.spec.ts
deleted file mode 100644
index d415bc749e..0000000000
--- a/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.spec.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*!
- * @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, ComponentFixture } from '@angular/core/testing';
-import { LibraryNameColumnComponent } from './library-name-column.component';
-
-describe('LibraryNameColumnComponent', () => {
- let fixture: ComponentFixture;
- let component: LibraryNameColumnComponent;
- let node;
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [LibraryNameColumnComponent]
- });
- node = {
- id: 'nodeId',
- path: {
- elements: []
- }
- };
- fixture = TestBed.createComponent(LibraryNameColumnComponent);
- component = fixture.componentInstance;
- });
-
- describe('makeLibraryTooltip()', () => {
- it('maps tooltip to description', () => {
- node.description = 'description';
- const tooltip = component.makeLibraryTooltip(node);
-
- expect(tooltip).toBe(node.description);
- });
-
- it('maps tooltip to description', () => {
- node.title = 'title';
- const tooltip = component.makeLibraryTooltip(node);
-
- expect(tooltip).toBe(node.title);
- });
-
- it('sets tooltip to empty string', () => {
- const tooltip = component.makeLibraryTooltip(node);
-
- expect(tooltip).toBe('');
- });
- });
-
- describe('makeLibraryTitle()', () => {
- it('sets title with id when duplicate nodes title exists in list', () => {
- node.title = 'title';
-
- const rows = [{ node: { entry: { id: 'some-id', title: 'title' } } }] as any[];
-
- const title = component.makeLibraryTitle(node, rows);
- expect(title).toContain('nodeId');
- });
-
- it('sets title when no duplicate nodes title exists in list', () => {
- node.title = 'title';
-
- const rows = [
- {
- node: { entry: { id: 'some-id', title: 'title-some-id' } }
- }
- ] as any[];
-
- const title = component.makeLibraryTitle(node, rows);
-
- expect(title).toBe('title');
- });
- });
-});
diff --git a/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts b/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts
index cc1176b4f8..e3383d6b88 100644
--- a/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts
+++ b/lib/content-services/src/lib/document-list/components/library-name-column/library-name-column.component.ts
@@ -16,13 +16,14 @@
*/
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core';
-import { NodeEntry, Site } from '@alfresco/js-api';
+import { NodeEntry } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
import { NodesApiService } from '../../../common/services/nodes-api.service';
import { BehaviorSubject } from 'rxjs';
import { AsyncPipe } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { NodeTooltipUtils } from '../../utils/node-tooltip.utils';
@Component({
selector: 'adf-library-name-column',
@@ -62,7 +63,10 @@ export class LibraryNameColumnComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
- constructor(private element: ElementRef, private nodesApiService: NodesApiService) {}
+ constructor(
+ private element: ElementRef,
+ private nodesApiService: NodesApiService
+ ) {}
ngOnInit() {
this.updateValue();
@@ -84,8 +88,9 @@ export class LibraryNameColumnComponent implements OnInit {
this.node = this.context.row.node;
const rows: Array = this.context.data.rows || [];
if (this.node?.entry) {
- this.displayText$.next(this.makeLibraryTitle(this.node.entry as any, rows));
- this.displayTooltip$.next(this.makeLibraryTooltip(this.node.entry));
+ const allEntries = rows.map((row: ShareDataRow) => row.node.entry);
+ this.displayText$.next(NodeTooltipUtils.getLibraryTitle(this.node.entry, allEntries));
+ this.displayTooltip$.next(NodeTooltipUtils.getLibraryTooltip(this.node));
}
}
@@ -99,23 +104,4 @@ export class LibraryNameColumnComponent implements OnInit {
})
);
}
-
- makeLibraryTooltip(library: any): string {
- const { description, title } = library;
-
- return description || title || '';
- }
-
- makeLibraryTitle(library: Site, rows: Array): string {
- const entries = rows.map((row: ShareDataRow) => row.node.entry);
- const { title, id } = library;
-
- let isDuplicate = false;
-
- if (entries) {
- isDuplicate = entries.some((entry: any) => entry.id !== id && entry.title === title);
- }
-
- return isDuplicate ? `${title} (${id})` : `${title}`;
- }
}
diff --git a/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.spec.ts b/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.spec.ts
index 11fb4fdb4f..80175a2692 100644
--- a/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.spec.ts
+++ b/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.spec.ts
@@ -35,11 +35,10 @@ describe('LibraryRoleColumnComponent', () => {
row: { node: { entry: { role: 'SiteManager' } } }
};
- let value = '';
- component.displayText$.subscribe((val) => (value = val));
-
+ component.ngOnInit();
fixture.detectChanges();
- expect(value).toBe('LIBRARY.ROLE.MANAGER');
+
+ expect(component.displayText()).toBe('LIBRARY.ROLE.MANAGER');
});
it('should render Collaborator', () => {
@@ -47,11 +46,10 @@ describe('LibraryRoleColumnComponent', () => {
row: { node: { entry: { role: 'SiteCollaborator' } } }
};
- let value = '';
- component.displayText$.subscribe((val) => (value = val));
-
+ component.ngOnInit();
fixture.detectChanges();
- expect(value).toBe('LIBRARY.ROLE.COLLABORATOR');
+
+ expect(component.displayText()).toBe('LIBRARY.ROLE.COLLABORATOR');
});
it('should render Contributor', () => {
@@ -59,11 +57,10 @@ describe('LibraryRoleColumnComponent', () => {
row: { node: { entry: { role: 'SiteContributor' } } }
};
- let value = '';
- component.displayText$.subscribe((val) => (value = val));
-
+ component.ngOnInit();
fixture.detectChanges();
- expect(value).toBe('LIBRARY.ROLE.CONTRIBUTOR');
+
+ expect(component.displayText()).toBe('LIBRARY.ROLE.CONTRIBUTOR');
});
it('should render Consumer', () => {
@@ -71,11 +68,10 @@ describe('LibraryRoleColumnComponent', () => {
row: { node: { entry: { role: 'SiteConsumer' } } }
};
- let value = '';
- component.displayText$.subscribe((val) => (value = val));
-
+ component.ngOnInit();
fixture.detectChanges();
- expect(value).toBe('LIBRARY.ROLE.CONSUMER');
+
+ expect(component.displayText()).toBe('LIBRARY.ROLE.CONSUMER');
});
it('should not render text for unknown', () => {
@@ -83,11 +79,10 @@ describe('LibraryRoleColumnComponent', () => {
row: { node: { entry: { role: 'ROLE' } } }
};
- let value = '';
- component.displayText$.subscribe((val) => (value = val));
-
+ component.ngOnInit();
fixture.detectChanges();
- expect(value).toBe('LIBRARY.ROLE.NONE');
+
+ expect(component.displayText()).toBe('LIBRARY.ROLE.NONE');
});
it('should take role from obj when node entry role is not provided', () => {
@@ -98,10 +93,9 @@ describe('LibraryRoleColumnComponent', () => {
}
};
- let value = '';
- component.displayText$.subscribe((val) => (value = val));
-
+ component.ngOnInit();
fixture.detectChanges();
- expect(value).toBe('LIBRARY.ROLE.MANAGER');
+
+ expect(component.displayText()).toBe('LIBRARY.ROLE.MANAGER');
});
});
diff --git a/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts b/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts
index a6ce11f193..b2e44f5590 100644
--- a/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts
+++ b/lib/content-services/src/lib/document-list/components/library-role-column/library-role-column.component.ts
@@ -15,21 +15,19 @@
* limitations under the License.
*/
-import { ChangeDetectionStrategy, Component, DestroyRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core';
-import { BehaviorSubject } from 'rxjs';
+import { ChangeDetectionStrategy, Component, computed, DestroyRef, inject, Input, OnInit, signal, ViewEncapsulation } from '@angular/core';
import { Site } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
import { NodesApiService } from '../../../common/services/nodes-api.service';
-import { AsyncPipe } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'adf-library-role-column',
- imports: [AsyncPipe, TranslatePipe],
+ imports: [TranslatePipe],
template: `
-
- {{ displayText$ | async | translate }}
+
+ {{ displayText() | translate }}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -40,7 +38,23 @@ export class LibraryRoleColumnComponent implements OnInit {
@Input({ required: true })
context: any;
- displayText$ = new BehaviorSubject('');
+ private readonly role = signal(undefined);
+
+ readonly displayText = computed(() => {
+ const roleValue = this.role();
+ switch (roleValue) {
+ case Site.RoleEnum.SiteManager:
+ return 'LIBRARY.ROLE.MANAGER';
+ case Site.RoleEnum.SiteCollaborator:
+ return 'LIBRARY.ROLE.COLLABORATOR';
+ case Site.RoleEnum.SiteContributor:
+ return 'LIBRARY.ROLE.CONTRIBUTOR';
+ case Site.RoleEnum.SiteConsumer:
+ return 'LIBRARY.ROLE.CONSUMER';
+ default:
+ return 'LIBRARY.ROLE.NONE';
+ }
+ });
private readonly destroyRef = inject(DestroyRef);
@@ -63,23 +77,7 @@ export class LibraryRoleColumnComponent implements OnInit {
}
protected updateValue() {
- const role = this.context.row.node?.entry.role ?? this.context.row.obj.role;
- switch (role) {
- case Site.RoleEnum.SiteManager:
- this.displayText$.next('LIBRARY.ROLE.MANAGER');
- break;
- case Site.RoleEnum.SiteCollaborator:
- this.displayText$.next('LIBRARY.ROLE.COLLABORATOR');
- break;
- case Site.RoleEnum.SiteContributor:
- this.displayText$.next('LIBRARY.ROLE.CONTRIBUTOR');
- break;
- case Site.RoleEnum.SiteConsumer:
- this.displayText$.next('LIBRARY.ROLE.CONSUMER');
- break;
- default:
- this.displayText$.next('LIBRARY.ROLE.NONE');
- break;
- }
+ const roleValue = this.context.row.node?.entry.role ?? this.context.row.obj.role;
+ this.role.set(roleValue);
}
}
diff --git a/lib/content-services/src/lib/document-list/components/name-column/name-column.component.spec.ts b/lib/content-services/src/lib/document-list/components/name-column/name-column.component.spec.ts
index 9177295d51..0bfee3bedd 100644
--- a/lib/content-services/src/lib/document-list/components/name-column/name-column.component.spec.ts
+++ b/lib/content-services/src/lib/document-list/components/name-column/name-column.component.spec.ts
@@ -18,8 +18,13 @@
import { NameColumnComponent } from './name-column.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { skip } from 'rxjs/operators';
+import { NodeEntry } from '@alfresco/js-api';
describe('NameColumnComponent', () => {
+ const nodeName = 'node-name';
+ const nodeTitle = 'node-title';
+ const nodeDescription = 'node-description';
+
let fixture: ComponentFixture;
let context: any;
let component: NameColumnComponent;
@@ -42,22 +47,159 @@ describe('NameColumnComponent', () => {
component.context = context;
});
- it('should set the display value based on default key', (done) => {
- component.displayText$.pipe(skip(1)).subscribe((value) => {
- expect(value).toBe('name');
- done();
+ describe('Display Text', () => {
+ it('should set the display value based on default key', (done) => {
+ component.displayText$.pipe(skip(1)).subscribe((value) => {
+ expect(value).toBe('name');
+ done();
+ });
+
+ component.ngOnInit();
});
- component.ngOnInit();
+ it('should set the display value based on the custom key', (done) => {
+ component.key = 'title';
+ component.displayText$.pipe(skip(1)).subscribe((value) => {
+ expect(value).toBe('title');
+ done();
+ });
+
+ component.ngOnInit();
+ });
});
- it('should set the display value based on the custom key', (done) => {
- component.key = 'title';
- component.displayText$.pipe(skip(1)).subscribe((value) => {
- expect(value).toBe('title');
- done();
+ describe('Tooltip Logic', () => {
+ it('should return null when missing node', () => {
+ component.context.row.node = null;
+ component.ngOnInit();
+ expect(component.tooltip()).toBe(null);
});
- component.ngOnInit();
+ it('should return null when missing node entry', () => {
+ component.context.row.node = {} as any;
+ component.ngOnInit();
+ expect(component.tooltip()).toBe(null);
+ });
+
+ it('should use title and description when all fields present', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': nodeTitle,
+ 'cm:description': nodeDescription
+ }
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(`${nodeTitle}\n${nodeDescription}`);
+ });
+
+ it('should use name when other properties are missing', () => {
+ const node = {
+ entry: {
+ name: nodeName
+ }
+ } as NodeEntry;
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(nodeName);
+ });
+
+ it('should display name when title and description are missing', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {}
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(nodeName);
+ });
+
+ it('should use name and description when title is missing', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': null,
+ 'cm:description': nodeDescription
+ }
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(`${nodeName}\n${nodeDescription}`);
+ });
+
+ it('should use name and title when description is missing', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': nodeTitle,
+ 'cm:description': null
+ }
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(`${nodeName}\n${nodeTitle}`);
+ });
+
+ it('should use name if name and description are the same', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': null,
+ 'cm:description': nodeName
+ }
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(nodeName);
+ });
+
+ it('should use name if name and title are the same', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': nodeName,
+ 'cm:description': null
+ }
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(nodeName);
+ });
+
+ it('should use name if all values are the same', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': nodeName,
+ 'cm:description': nodeName
+ }
+ }
+ };
+ component.context.row.node = node;
+ component.ngOnInit();
+ const tooltip = component.tooltip();
+ expect(tooltip).toBe(nodeName);
+ });
});
});
diff --git a/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts b/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts
index 9b01179523..e0fc759824 100644
--- a/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts
+++ b/lib/content-services/src/lib/document-list/components/name-column/name-column.component.ts
@@ -15,19 +15,19 @@
* limitations under the License.
*/
-import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core';
+import { ChangeDetectionStrategy, Component, computed, DestroyRef, ElementRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api';
import { BehaviorSubject } from 'rxjs';
import { NodesApiService } from '../../../common/services/nodes-api.service';
import { ShareDataRow } from '../../data/share-data-row.model';
import { AsyncPipe } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
-import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { NodeTooltipUtils } from '../../utils/node-tooltip.utils';
@Component({
selector: 'adf-name-column',
- imports: [AsyncPipe, TranslatePipe, NodeNameTooltipPipe],
+ imports: [AsyncPipe, TranslatePipe],
template: `
('');
node: NodeEntry;
+ readonly tooltip = computed(() => NodeTooltipUtils.getNodeTooltip(this.node));
+
private readonly destroyRef = inject(DestroyRef);
- constructor(private element: ElementRef, private nodesApiService: NodesApiService) {}
+ constructor(
+ private element: ElementRef,
+ private nodesApiService: NodesApiService
+ ) {}
ngOnInit() {
this.updateValue();
@@ -85,8 +91,8 @@ export class NameColumnComponent implements OnInit {
this.node = this.context.row.node;
if (this.node?.entry) {
- const displayText = this.context.row.getValue(this.key);
- this.displayText$.next(displayText || this.node.entry.id);
+ const displayValue = this.context.row.getValue(this.key);
+ this.displayText$.next(displayValue || this.node.entry.id);
}
}
diff --git a/lib/content-services/src/lib/document-list/components/trashcan-name-column/trashcan-name-column.component.ts b/lib/content-services/src/lib/document-list/components/trashcan-name-column/trashcan-name-column.component.ts
index 3b3b90ca86..3a80b21ea4 100644
--- a/lib/content-services/src/lib/document-list/components/trashcan-name-column/trashcan-name-column.component.ts
+++ b/lib/content-services/src/lib/document-list/components/trashcan-name-column/trashcan-name-column.component.ts
@@ -15,36 +15,29 @@
* limitations under the License.
*/
-import { ChangeDetectionStrategy, Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
+import { ChangeDetectionStrategy, Component, computed, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
-import { CommonModule } from '@angular/common';
-import { NodeNameTooltipPipe } from '../../../pipes/node-name-tooltip.pipe';
+import { NodeTooltipUtils } from '../../utils/node-tooltip.utils';
@Component({
selector: 'adf-trashcan-name-column',
- imports: [CommonModule, NodeNameTooltipPipe],
- template: `
-
- {{ displayText }}
-
-
- {{ displayText }}
-
- `,
+ template: `{{ displayText }}`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-datatable-content-cell adf-trashcan-name-column' }
})
export class TrashcanNameColumnComponent implements OnInit {
@Input({ required: true })
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
context: any;
isLibrary = false;
displayText: string;
- displayTooltip: string;
node: NodeEntry;
+ readonly tooltip = computed(() => (this.isLibrary ? NodeTooltipUtils.getLibraryTooltip(this.node) : NodeTooltipUtils.getNodeTooltip(this.node)));
+
ngOnInit() {
this.node = this.context.row.node;
const rows: Array = this.context.data.rows || [];
@@ -53,27 +46,11 @@ export class TrashcanNameColumnComponent implements OnInit {
this.isLibrary = this.node.entry.nodeType === 'st:site';
if (this.isLibrary) {
- const { properties } = this.node.entry;
-
- this.displayText = this.makeLibraryTitle(this.node.entry, rows);
- this.displayTooltip = properties['cm:description'] || properties['cm:title'];
+ const allEntries = rows.map((row) => row.node.entry);
+ this.displayText = NodeTooltipUtils.getLibraryTitle(this.node.entry, allEntries);
} else {
this.displayText = this.node.entry.name || this.node.entry.id;
}
}
}
-
- makeLibraryTitle(library: any, rows: Array): string {
- const entries = rows.map((r: ShareDataRow) => r.node.entry);
- const { id } = library;
- const title = library.properties['cm:title'];
-
- let isDuplicate = false;
-
- if (entries) {
- isDuplicate = entries.some((entry: any) => entry.id !== id && entry.properties['cm:title'] === title);
- }
-
- return isDuplicate ? `${library.properties['cm:title']} (${library.name})` : `${library.properties['cm:title']}`;
- }
}
diff --git a/lib/content-services/src/lib/document-list/public-api.ts b/lib/content-services/src/lib/document-list/public-api.ts
index 6694b4b950..8fec33d7ba 100644
--- a/lib/content-services/src/lib/document-list/public-api.ts
+++ b/lib/content-services/src/lib/document-list/public-api.ts
@@ -49,4 +49,7 @@ export * from './models/node-action.enum';
export * from './interfaces/document-list-loader.interface';
+// utils
+export * from './utils/node-tooltip.utils';
+
export * from './document-list.module';
diff --git a/lib/content-services/src/lib/document-list/utils/node-tooltip.utils.spec.ts b/lib/content-services/src/lib/document-list/utils/node-tooltip.utils.spec.ts
new file mode 100644
index 0000000000..2049382d7b
--- /dev/null
+++ b/lib/content-services/src/lib/document-list/utils/node-tooltip.utils.spec.ts
@@ -0,0 +1,289 @@
+/*!
+ * @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 { NodeEntry } from '@alfresco/js-api';
+import { NodeTooltipUtils } from './node-tooltip.utils';
+
+describe('NodeTooltipUtils', () => {
+ const nodeName = 'node-name';
+ const nodeTitle = 'node-title';
+ const nodeDescription = 'node-description';
+
+ describe('getNodeTooltip', () => {
+ it('should return null when node is null', () => {
+ const result = NodeTooltipUtils.getNodeTooltip(null as any);
+ expect(result).toBe(null);
+ });
+
+ it('should return null when node entry is missing', () => {
+ const node = {} as NodeEntry;
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe(null);
+ });
+
+ it('should return name only when title and description are missing', () => {
+ const node: any = {
+ entry: {
+ name: nodeName
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe(nodeName);
+ });
+
+ it('should return name only when properties object is empty', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {}
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe(nodeName);
+ });
+
+ it('should return title and description when both are present', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': nodeTitle,
+ 'cm:description': nodeDescription
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe(`${nodeTitle}\n${nodeDescription}`);
+ });
+
+ it('should return name and title when only title is present', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:title': nodeTitle
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe(`${nodeName}\n${nodeTitle}`);
+ });
+
+ it('should return name and description when only description is present', () => {
+ const node: any = {
+ entry: {
+ name: nodeName,
+ properties: {
+ 'cm:description': nodeDescription
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe(`${nodeName}\n${nodeDescription}`);
+ });
+
+ it('should remove case-insensitive duplicates', () => {
+ const node: any = {
+ entry: {
+ name: 'Same-Name',
+ properties: {
+ 'cm:title': 'same-name'
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ expect(result).toBe('Same-Name');
+ });
+
+ it('should preserve order when removing duplicates', () => {
+ const node: any = {
+ entry: {
+ name: 'First',
+ properties: {
+ 'cm:title': 'Second',
+ 'cm:description': 'FIRST'
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getNodeTooltip(node);
+ // When both title and description exist, shows "title\ndescription"
+ // 'FIRST' is a case-insensitive duplicate of 'First', but since we're using title+description logic,
+ // the duplicate check happens between 'Second' and 'FIRST', so 'FIRST' remains
+ expect(result).toBe('Second\nFIRST');
+ });
+ });
+
+ describe('getLibraryTooltip', () => {
+ it('should return empty string when node is null', () => {
+ const result = NodeTooltipUtils.getLibraryTooltip(null as any);
+ expect(result).toBe('');
+ });
+
+ it('should return empty string when node entry is missing', () => {
+ const node = {} as NodeEntry;
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('');
+ });
+
+ it('should return description when available', () => {
+ const node: any = {
+ entry: {
+ properties: {
+ 'cm:title': 'Library Title',
+ 'cm:description': 'Library Description'
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('Library Description');
+ });
+
+ it('should return title when description is not available', () => {
+ const node: any = {
+ entry: {
+ properties: {
+ 'cm:title': 'Library Title'
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('Library Title');
+ });
+
+ it('should return empty string when neither description nor title are available', () => {
+ const node: any = {
+ entry: {
+ properties: {}
+ }
+ };
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('');
+ });
+
+ it('should handle direct properties (description and title)', () => {
+ const node: any = {
+ entry: {
+ description: 'Direct Description',
+ title: 'Direct Title'
+ }
+ };
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('Direct Description');
+ });
+
+ it('should prefer direct description over cm:description', () => {
+ const node: any = {
+ entry: {
+ description: 'Direct Description',
+ properties: {
+ 'cm:description': 'CM Description'
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('Direct Description');
+ });
+
+ it('should fall back to cm:description when direct description is not available', () => {
+ const node: any = {
+ entry: {
+ properties: {
+ 'cm:description': 'CM Description'
+ }
+ }
+ };
+ const result = NodeTooltipUtils.getLibraryTooltip(node);
+ expect(result).toBe('CM Description');
+ });
+ });
+
+ describe('getLibraryTitle', () => {
+ it('should return empty string when library is null', () => {
+ const result = NodeTooltipUtils.getLibraryTitle(null, []);
+ expect(result).toBe('');
+ });
+
+ it('should return title when no duplicates exist', () => {
+ const library = { id: 'lib1', title: 'Library Title' };
+ const allEntries = [
+ { id: 'lib2', title: 'Other Title' },
+ { id: 'lib3', title: 'Another Title' }
+ ];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ expect(result).toBe('Library Title');
+ });
+
+ it('should append ID when duplicate title exists', () => {
+ const library = { id: 'lib1', title: 'Duplicate Title' };
+ const allEntries = [
+ { id: 'lib2', title: 'Duplicate Title' },
+ { id: 'lib3', title: 'Other Title' }
+ ];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ expect(result).toBe('Duplicate Title (lib1)');
+ });
+
+ it('should append name when duplicate title exists and name is available', () => {
+ const library = { id: 'lib1', name: 'library-name', title: 'Duplicate Title' };
+ const allEntries = [{ id: 'lib2', title: 'Duplicate Title' }];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ expect(result).toBe('Duplicate Title (library-name)');
+ });
+
+ it('should handle cm:title properties', () => {
+ const library = {
+ id: 'lib1',
+ name: 'library-name',
+ properties: { 'cm:title': 'CM Title' }
+ };
+ const allEntries = [{ id: 'lib2', properties: { 'cm:title': 'CM Title' } }];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ expect(result).toBe('CM Title (library-name)');
+ });
+
+ it('should not append suffix when no duplicate exists', () => {
+ const library = { id: 'lib1', name: 'library-name', title: 'Unique Title' };
+ const allEntries = [{ id: 'lib2', title: 'Other Title' }];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ expect(result).toBe('Unique Title');
+ });
+
+ it('should return name when title is missing', () => {
+ const library = { id: 'lib1', name: 'library-name' };
+ const allEntries = [];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ // Returns name when available, otherwise id
+ expect(result).toBe('library-name');
+ });
+
+ it('should handle empty entries array', () => {
+ const library = { id: 'lib1', title: 'Library Title' };
+ const result = NodeTooltipUtils.getLibraryTitle(library, []);
+ expect(result).toBe('Library Title');
+ });
+
+ it('should not consider same library as duplicate', () => {
+ const library = { id: 'lib1', title: 'Library Title' };
+ const allEntries = [
+ { id: 'lib1', title: 'Library Title' },
+ { id: 'lib2', title: 'Other Title' }
+ ];
+ const result = NodeTooltipUtils.getLibraryTitle(library, allEntries);
+ expect(result).toBe('Library Title');
+ });
+ });
+});
diff --git a/lib/content-services/src/lib/document-list/utils/node-tooltip.utils.ts b/lib/content-services/src/lib/document-list/utils/node-tooltip.utils.ts
new file mode 100644
index 0000000000..ee13ff2d78
--- /dev/null
+++ b/lib/content-services/src/lib/document-list/utils/node-tooltip.utils.ts
@@ -0,0 +1,144 @@
+/*!
+ * @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 { NodeEntry } from '@alfresco/js-api';
+
+/**
+ * Utility class for generating node and library tooltips based on node properties
+ */
+export class NodeTooltipUtils {
+ /**
+ * Generates a tooltip string for a node based on its name, title, and description properties.
+ * The tooltip logic follows these rules:
+ * - If both title and description exist: shows "title\ndescription"
+ * - If only title exists: shows "name\ntitle"
+ * - If only description exists: shows "name\ndescription"
+ * - If neither exists: shows "name"
+ * - Removes case-insensitive duplicates while preserving order
+ *
+ * @param node - The node entry to generate tooltip for
+ * @returns The tooltip string with newline-separated lines, or null if node is invalid
+ */
+ static getNodeTooltip(node: NodeEntry): string | null {
+ if (!node?.entry) {
+ return null;
+ }
+
+ const {
+ entry: { properties, name }
+ } = node;
+
+ const title = properties?.['cm:title'];
+ const description = properties?.['cm:description'];
+
+ // Build lines array based on available properties
+ const lines: string[] = [];
+
+ // Determine first line: title if available and different from name, otherwise name
+ if (title && description) {
+ lines.push(title, description);
+ } else if (title) {
+ lines.push(name, title);
+ } else if (description) {
+ lines.push(name, description);
+ } else {
+ lines.push(name);
+ }
+
+ // Remove case-insensitive duplicates while preserving order
+ return this.removeDuplicates(lines).join('\n');
+ }
+
+ /**
+ * Generates a tooltip string for a library (site) node.
+ * Returns description if available, otherwise title, otherwise empty string.
+ *
+ * @param node - The node entry to generate tooltip for
+ * @returns The tooltip string, or empty string if no description or title is available
+ */
+ static getLibraryTooltip(node: NodeEntry): string {
+ if (!node?.entry) {
+ return '';
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const { properties, description, title } = node.entry as any;
+
+ // Check both direct properties and cm: properties for compatibility
+ const desc = description || properties?.['cm:description'];
+ const ttl = title || properties?.['cm:title'];
+
+ return desc || ttl || '';
+ }
+
+ /**
+ * Generates a display title for a library (site) node.
+ * If there are duplicate titles in the list, appends the library ID/name in parentheses.
+ *
+ * @param library - The library entry object
+ * @param allEntries - Array of all entries to check for duplicates
+ * @returns The display title, with ID/name appended if duplicate exists
+ */
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ static getLibraryTitle(library: any, allEntries: any[]): string {
+ if (!library) {
+ return '';
+ }
+
+ // Support both direct properties and cm: properties
+ const libraryId = library.id;
+ const libraryName = library.name;
+ const libraryTitle = library.title || library.properties?.['cm:title'];
+
+ if (!libraryTitle) {
+ return libraryName || libraryId || '';
+ }
+
+ // Check if there are duplicate titles in the list
+ let isDuplicate = false;
+
+ if (allEntries && allEntries.length > 0) {
+ isDuplicate = allEntries.some((entry) => {
+ const entryId = entry.id;
+ const entryTitle = entry.title || entry.properties?.['cm:title'];
+ return entryId !== libraryId && entryTitle === libraryTitle;
+ });
+ }
+
+ // If duplicate, append the ID or name in parentheses
+ const suffix = libraryName || libraryId;
+ return isDuplicate && suffix ? `${libraryTitle} (${suffix})` : libraryTitle;
+ }
+
+ /**
+ * Removes case-insensitive duplicate strings from an array while preserving order
+ *
+ * @param lines - Array of strings to deduplicate
+ * @returns Array with duplicates removed
+ */
+ private static removeDuplicates(lines: string[]): string[] {
+ const seen = new Set();
+ return lines.filter((line) => {
+ const lowerLine = line.toLowerCase();
+ if (seen.has(lowerLine)) {
+ return false;
+ }
+ seen.add(lowerLine);
+ return true;
+ });
+ }
+}
diff --git a/lib/content-services/src/lib/pipes/content-pipe.module.ts b/lib/content-services/src/lib/pipes/content-pipe.module.ts
deleted file mode 100644
index 3ae4bec186..0000000000
--- a/lib/content-services/src/lib/pipes/content-pipe.module.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/*!
- * @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 { NgModule } from '@angular/core';
-import { NodeNameTooltipPipe } from './node-name-tooltip.pipe';
-
-export const CONTENT_PIPES = [NodeNameTooltipPipe] as const;
-
-/**
- * @deprecated Use the individual pipe modules instead.
- */
-@NgModule({
- imports: [...CONTENT_PIPES],
- providers: [...CONTENT_PIPES],
- exports: [...CONTENT_PIPES]
-})
-export class ContentPipeModule {}
diff --git a/lib/content-services/src/lib/pipes/index.ts b/lib/content-services/src/lib/pipes/index.ts
deleted file mode 100644
index f22da90e09..0000000000
--- a/lib/content-services/src/lib/pipes/index.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*!
- * @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 * from './public-api';
diff --git a/lib/content-services/src/lib/pipes/node-name-tooltip.pipe.spec.ts b/lib/content-services/src/lib/pipes/node-name-tooltip.pipe.spec.ts
deleted file mode 100644
index 95b3dd34a3..0000000000
--- a/lib/content-services/src/lib/pipes/node-name-tooltip.pipe.spec.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-/*!
- * @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 { NodeEntry } from '@alfresco/js-api';
-import { NodeNameTooltipPipe } from './node-name-tooltip.pipe';
-
-describe('NodeNameTooltipPipe', () => {
- const nodeName = 'node-name';
- const nodeTitle = 'node-title';
- const nodeDescription = 'node-description';
-
- let pipe: NodeNameTooltipPipe;
-
- beforeEach(() => {
- pipe = new NodeNameTooltipPipe();
- });
-
- it('should not transform when missing node', () => {
- expect(pipe.transform(null)).toBe(null);
- });
-
- it('should not transform when missing node entry', () => {
- expect(pipe.transform({} as any)).toBe(null);
- });
-
- it('should use title and description when all fields present', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {
- 'cm:title': nodeTitle,
- 'cm:description': nodeDescription
- }
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(`${nodeTitle}\n${nodeDescription}`);
- });
-
- it('should use name when other properties are missing', () => {
- const node = {
- entry: {
- name: nodeName
- }
- } as NodeEntry;
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(nodeName);
- });
-
- it('should display name when title and description are missing', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {}
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(nodeName);
- });
-
- it('should use name and description when title is missing', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {
- 'cm:title': null,
- 'cm:description': nodeDescription
- }
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(`${nodeName}\n${nodeDescription}`);
- });
-
- it('should use name and title when description is missing', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {
- 'cm:title': nodeTitle,
- 'cm:description': null
- }
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(`${nodeName}\n${nodeTitle}`);
- });
-
- it('should use name if name and description are the same', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {
- 'cm:title': null,
- 'cm:description': nodeName
- }
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(nodeName);
- });
-
- it('should use name if name and title are the same', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {
- 'cm:title': nodeName,
- 'cm:description': null
- }
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(nodeName);
- });
-
- it('should use name if all values are the same', () => {
- const node: any = {
- entry: {
- name: nodeName,
- properties: {
- 'cm:title': nodeName,
- 'cm:description': nodeName
- }
- }
- };
- const tooltip = pipe.transform(node);
- expect(tooltip).toBe(nodeName);
- });
-});
diff --git a/lib/content-services/src/lib/pipes/node-name-tooltip.pipe.ts b/lib/content-services/src/lib/pipes/node-name-tooltip.pipe.ts
deleted file mode 100644
index 5db4959969..0000000000
--- a/lib/content-services/src/lib/pipes/node-name-tooltip.pipe.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-/*!
- * @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 { Pipe, PipeTransform } from '@angular/core';
-import { NodeEntry } from '@alfresco/js-api';
-
-@Pipe({
- name: 'adfNodeNameTooltip'
-})
-export class NodeNameTooltipPipe implements PipeTransform {
- transform(node: NodeEntry): string {
- if (node) {
- return this.getNodeTooltip(node);
- }
- return null;
- }
-
- private containsLine(lines: string[], line: string): boolean {
- return lines.some((item: string) => item.toLowerCase() === line.toLowerCase());
- }
-
- private removeDuplicateLines(lines: string[]): string[] {
- const reducer = (acc: string[], line: string): string[] => {
- if (!this.containsLine(acc, line)) {
- acc.push(line);
- }
- return acc;
- };
-
- return lines.reduce(reducer, []);
- }
-
- private getNodeTooltip(node: NodeEntry): string {
- if (!node?.entry) {
- return null;
- }
-
- const {
- entry: { properties, name }
- } = node;
- const lines = [name];
-
- if (properties) {
- const { 'cm:title': title, 'cm:description': description } = properties;
-
- if (title && description) {
- lines[0] = title;
- lines[1] = description;
- }
-
- if (title) {
- lines[1] = title;
- }
-
- if (description) {
- lines[1] = description;
- }
- }
-
- return this.removeDuplicateLines(lines).join(`\n`);
- }
-}
diff --git a/lib/content-services/src/lib/pipes/public-api.ts b/lib/content-services/src/lib/pipes/public-api.ts
deleted file mode 100644
index c6280cde7e..0000000000
--- a/lib/content-services/src/lib/pipes/public-api.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/*!
- * @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 * from './node-name-tooltip.pipe';
-export * from './content-pipe.module';
diff --git a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts
index a338a5842c..da368594a4 100644
--- a/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts
+++ b/lib/content-services/src/lib/search/components/search-date-range-tabbed/search-date-range/search-date-range.component.ts
@@ -79,6 +79,8 @@ export class SearchDateRangeComponent implements OnInit {
@Output()
valid = new EventEmitter();
+ private readonly formBuilder = inject(FormBuilder);
+
form = this.formBuilder.group({
dateRangeType: DateRangeType.ANY,
inLastValueType: InLastDateType.DAYS,
@@ -96,7 +98,6 @@ export class SearchDateRangeComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
constructor(
- private formBuilder: FormBuilder,
private userPreferencesService: UserPreferencesService,
private dateAdapter: DateAdapter,
@Inject(MAT_DATE_FORMATS) private dateFormatConfig: MatDateFormats
diff --git a/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts b/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts
index 222e24e5fc..c541d1a02c 100644
--- a/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts
+++ b/lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts
@@ -59,6 +59,8 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, Sear
autocompleteOptions: AutocompleteOption[] = [];
preselectedOptions: AutocompleteOption[] = [];
+ private readonly formBuilder = inject(FormBuilder);
+
private _form = this.formBuilder.nonNullable.group({
fileSizeOperator: FileSizeOperator.AT_LEAST,
fileSize: undefined,
@@ -106,10 +108,7 @@ export class SearchPropertiesComponent implements OnInit, AfterViewChecked, Sear
private readonly destroyRef = inject(DestroyRef);
- constructor(
- private readonly formBuilder: FormBuilder,
- private readonly translateService: TranslateService
- ) {}
+ constructor(private readonly translateService: TranslateService) {}
ngOnInit() {
if (this.settings) {
diff --git a/lib/content-services/src/public-api.ts b/lib/content-services/src/public-api.ts
index 3762d1f1b4..0d1542b78a 100644
--- a/lib/content-services/src/public-api.ts
+++ b/lib/content-services/src/public-api.ts
@@ -36,7 +36,6 @@ export * from './lib/node-comments/index';
export * from './lib/new-version-uploader';
export * from './lib/interfaces/index';
export * from './lib/version-compatibility/index';
-export * from './lib/pipes/index';
export * from './lib/common/index';
export * from './lib/tree/index';
export * from './lib/category/index';
diff --git a/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts b/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts
index 9809b7a676..0e413fef35 100644
--- a/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts
+++ b/lib/core/src/lib/auth/oidc/oidc-authentication.service.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Injectable } from '@angular/core';
+import { Injectable, inject } from '@angular/core';
import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { Observable, defer, EMPTY, combineLatest } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
@@ -33,17 +33,7 @@ import { HttpHeaders } from '@angular/common/http';
providedIn: 'root'
})
export class OidcAuthenticationService extends BaseAuthenticationService {
- constructor(
- appConfig: AppConfigService,
- cookie: CookieService,
- private jwtHelperService: JwtHelperService,
- private authStorage: OAuthStorage,
- private oauthService: OAuthService,
- private readonly authConfig: AuthConfigService,
- private readonly auth: AuthService
- ) {
- super(appConfig, cookie);
- }
+ private readonly auth = inject(AuthService);
/**
* Observable that determines whether an SSO login should be performed.
@@ -56,6 +46,17 @@ export class OidcAuthenticationService extends BaseAuthenticationService {
map(([authenticated, isDiscoveryDocumentLoaded]) => !authenticated && isDiscoveryDocumentLoaded)
);
+ constructor(
+ appConfig: AppConfigService,
+ cookie: CookieService,
+ private jwtHelperService: JwtHelperService,
+ private authStorage: OAuthStorage,
+ private oauthService: OAuthService,
+ private readonly authConfig: AuthConfigService
+ ) {
+ super(appConfig, cookie);
+ }
+
isLoggedIn(): boolean {
return this.oauthService.hasValidAccessToken() && this.oauthService.hasValidIdToken();
}
diff --git a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts
index bc2a33ad0f..3c7e4959b6 100644
--- a/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts
+++ b/lib/core/src/lib/card-view/components/card-view-boolitem/card-view-boolitem.component.ts
@@ -39,7 +39,7 @@ import { MatError } from '@angular/material/form-field';
})
export class CardViewBoolItemComponent extends BaseCardView {
@Input()
- editable: boolean;
+ declare editable: boolean;
private _error: string;
diff --git a/lib/core/src/lib/card-view/models/card-view-arrayitem.model.ts b/lib/core/src/lib/card-view/models/card-view-arrayitem.model.ts
index 3f70fbdcc3..ac7fb3eb98 100644
--- a/lib/core/src/lib/card-view/models/card-view-arrayitem.model.ts
+++ b/lib/core/src/lib/card-view/models/card-view-arrayitem.model.ts
@@ -28,7 +28,7 @@ export interface CardViewArrayItem {
export class CardViewArrayItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'array';
- value: Observable;
+ declare value: Observable;
noOfItemsToDisplay: number;
constructor(cardViewArrayItemProperties: CardViewArrayItemProperties) {
diff --git a/lib/core/src/lib/card-view/models/card-view-boolitem.model.ts b/lib/core/src/lib/card-view/models/card-view-boolitem.model.ts
index fcd9a726fe..426cfce346 100644
--- a/lib/core/src/lib/card-view/models/card-view-boolitem.model.ts
+++ b/lib/core/src/lib/card-view/models/card-view-boolitem.model.ts
@@ -23,7 +23,7 @@ import { CardViewBoolItemProperties } from '../interfaces/card-view.interfaces';
export class CardViewBoolItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type = 'bool';
value = false;
- default: boolean;
+ declare default: boolean;
constructor(cardViewBoolItemProperties: CardViewBoolItemProperties) {
super(cardViewBoolItemProperties);
diff --git a/lib/core/src/lib/card-view/models/card-view-mapitem.model.ts b/lib/core/src/lib/card-view/models/card-view-mapitem.model.ts
index b3b5baf2b1..e7e803171b 100644
--- a/lib/core/src/lib/card-view/models/card-view-mapitem.model.ts
+++ b/lib/core/src/lib/card-view/models/card-view-mapitem.model.ts
@@ -21,7 +21,7 @@ import { CardViewBaseItemModel } from './card-view-baseitem.model';
export class CardViewMapItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type = 'map';
- value: Map;
+ declare value: Map;
get displayValue() {
if (this.value && this.value.size > 0) {
diff --git a/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts b/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts
index 4c23659ecf..43eab9045c 100644
--- a/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts
+++ b/lib/core/src/lib/datatable/components/datatable-cell/datatable-cell.component.ts
@@ -21,7 +21,7 @@ import { DataRow } from '../../data/data-row.model';
import { DataTableAdapter } from '../../data/datatable-adapter';
import { BehaviorSubject } from 'rxjs';
import { DataTableService } from '../../services/datatable.service';
-import { CommonModule } from '@angular/common';
+import { AsyncPipe } from '@angular/common';
import { ClipboardDirective } from '../../../clipboard/clipboard.directive';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TruncatePipe } from '../../../pipes/truncate.pipe';
@@ -29,7 +29,7 @@ import { UserPreferencesService } from '../../../common/services/user-preference
@Component({
selector: 'adf-datatable-cell',
- imports: [CommonModule, ClipboardDirective, TruncatePipe],
+ imports: [ClipboardDirective, TruncatePipe, AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
@let value = value$ | async;
diff --git a/lib/core/src/lib/form/components/widgets/core/container.model.ts b/lib/core/src/lib/form/components/widgets/core/container.model.ts
index 0a103c95a0..ac3ca3b1ad 100644
--- a/lib/core/src/lib/form/components/widgets/core/container.model.ts
+++ b/lib/core/src/lib/form/components/widgets/core/container.model.ts
@@ -23,7 +23,7 @@ import { ContainerColumnModel } from './container-column.model';
import { FormFieldTypes } from './form-field-types';
export class ContainerModel extends FormWidgetModel {
- field: FormFieldModel;
+ declare field: FormFieldModel;
readonly columns: ContainerColumnModel[] = [];
isExpanded: boolean = true;
diff --git a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts
index 1539d75c16..b3e0249f6c 100644
--- a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts
+++ b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts
@@ -52,12 +52,12 @@ export class FormFieldModel extends FormWidgetModel {
private readonly defaultEmptyOptionName = 'Choose one...';
// model members
- fieldType: string;
- id: string;
- name: string;
- type: string;
+ declare fieldType: string;
+ declare id: string;
+ declare name: string;
+ declare type: string;
overrideId: boolean;
- tab: string;
+ declare tab: string;
rowspan: number = 1;
colspan: number = 1;
placeholder: string = null;
@@ -98,7 +98,7 @@ export class FormFieldModel extends FormWidgetModel {
variableConfig: VariableConfig;
schemaDefinition: DataColumn[];
externalProperty?: string;
- style?: string;
+ declare style?: string;
parent?: RepeatableSectionModel;
// container model members
diff --git a/lib/process-services-cloud/src/lib/models/task-list-sorting.model.ts b/lib/process-services-cloud/src/lib/models/task-list-sorting.model.ts
index 1ff3f39a73..592975ada3 100644
--- a/lib/process-services-cloud/src/lib/models/task-list-sorting.model.ts
+++ b/lib/process-services-cloud/src/lib/models/task-list-sorting.model.ts
@@ -28,14 +28,14 @@ export class TaskListCloudSortingModel {
}
export class TaskListRequestSortingModel extends TaskListCloudSortingModel {
- orderBy: string;
- direction: string;
+ declare orderBy: string;
+ declare direction: string;
isFieldProcessVariable: boolean;
processVariableData?: {
processDefinitionKey: string;
type: string;
- }
+ };
constructor(obj: TaskListRequestSortingModel) {
super(obj);
diff --git a/lib/process-services-cloud/src/lib/people/mock/people-cloud.mock.ts b/lib/process-services-cloud/src/lib/people/mock/people-cloud.mock.ts
index ba8dcd10e3..bbbda5207e 100644
--- a/lib/process-services-cloud/src/lib/people/mock/people-cloud.mock.ts
+++ b/lib/process-services-cloud/src/lib/people/mock/people-cloud.mock.ts
@@ -51,7 +51,7 @@ export const mockPreselectedFoodUsers = [
@Injectable()
export class IdentityUserServiceMock extends IdentityUserService {
- queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] };
+ declare queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] };
getCurrentUserInfo(): IdentityUserModel {
return mockKielbasaSausage;
diff --git a/lib/process-services-cloud/src/lib/process/process-list/models/process-list-sorting.model.ts b/lib/process-services-cloud/src/lib/process/process-list/models/process-list-sorting.model.ts
index 1bce1642eb..8adf920ed5 100644
--- a/lib/process-services-cloud/src/lib/process/process-list/models/process-list-sorting.model.ts
+++ b/lib/process-services-cloud/src/lib/process/process-list/models/process-list-sorting.model.ts
@@ -18,7 +18,7 @@
export class ProcessListCloudSortingModel {
orderBy: string;
direction: string;
- constructor(obj: any) {
+ constructor(obj: any) {
if (obj) {
this.orderBy = obj.orderBy;
this.direction = obj.direction;
@@ -26,14 +26,14 @@ export class ProcessListCloudSortingModel {
}
}
export class ProcessListRequestSortingModel extends ProcessListCloudSortingModel {
- orderBy: string;
- direction: string;
+ declare orderBy: string;
+ declare direction: string;
isFieldProcessVariable: boolean;
processVariableData?: {
processDefinitionKey: string;
type: string;
- }
+ };
constructor(obj: ProcessListRequestSortingModel) {
super(obj);
diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts
index 730a6f6071..b9b4ecf538 100644
--- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list/task-list-cloud.component.ts
@@ -260,7 +260,7 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent(false);
isLoading$ = combineLatest([this.isLoadingPreferences$, this.isReloadingSubject$]).pipe(
diff --git a/nx.json b/nx.json
index 54b012420c..48d436f790 100644
--- a/nx.json
+++ b/nx.json
@@ -18,6 +18,7 @@
},
"tasksRunnerOptions": {
"default": {
+ "runner": "nx/tasks-runners/default",
"options": {
"runtimeCacheInputs": ["node -v"]
}
diff --git a/package.json b/package.json
index 1e65b27c39..e503951fb5 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"version": "8.3.0",
"author": "Hyland Software, Inc. and its affiliates",
"scripts": {
- "prepare": "husky install",
+ "prepare": "husky",
"bundle:js-api": "nx run js-api:bundle",
"bundle:cli": "nx run cli:bundle",
"test:affected": "nx affected:test",