ACS-8772: Migrate node tooltip to Angular signals (#11498)

This commit is contained in:
Denys Vuika
2026-01-13 14:04:04 +00:00
committed by GitHub
parent d7c01d3558
commit 716ce228ce
38 changed files with 705 additions and 578 deletions
-1
View File
@@ -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) |
-34
View File
@@ -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
<!-- {% raw %} -->
```html
<data-column
key="name"
title="Name">
<ng-template let-value="value" let-context>
<span title="{{ context?.row?.obj | adfNodeNameTooltip }}">{{ value }}</span>
</ng-template>
</data-column>
```
<!-- {% endraw %} -->
## 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.
-1
View File
@@ -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)
@@ -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
@@ -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,
@@ -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;
@@ -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<LibraryNameColumnComponent>;
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');
});
});
});
@@ -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<ShareDataRow> = 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<ShareDataRow>): 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}`;
}
}
@@ -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');
});
});
@@ -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: `
<span class="adf-datatable-cell-value" title="{{ displayText$ | async | translate }}">
{{ displayText$ | async | translate }}
<span class="adf-datatable-cell-value" [title]="displayText() | translate">
{{ displayText() | translate }}
</span>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -40,7 +38,23 @@ export class LibraryRoleColumnComponent implements OnInit {
@Input({ required: true })
context: any;
displayText$ = new BehaviorSubject<string>('');
private readonly role = signal<string | undefined>(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);
}
}
@@ -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<NameColumnComponent>;
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);
});
});
});
@@ -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: `
<span
role="link"
@@ -39,7 +39,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
}
"
class="adf-datatable-cell-value"
title="{{ node | adfNodeNameTooltip }}"
[title]="tooltip()"
(click)="onClick()"
tabindex="0"
(keyup.enter)="onClick()"
@@ -53,6 +53,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
})
export class NameColumnComponent implements OnInit {
@Input({ required: true })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context: any;
@Input()
@@ -61,9 +62,14 @@ export class NameColumnComponent implements OnInit {
displayText$ = new BehaviorSubject<string>('');
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);
}
}
@@ -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: `
<ng-container *ngIf="!isLibrary">
<span class="adf-datatable-cell-value" title="{{ node | adfNodeNameTooltip }}">{{ displayText }}</span>
</ng-container>
<ng-container *ngIf="isLibrary">
<span class="adf-datatable-cell-value" title="{{ displayTooltip }}">{{ displayText }}</span>
</ng-container>
`,
template: `<span class="adf-datatable-cell-value" [title]="tooltip()">{{ displayText }}</span>`,
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<ShareDataRow> = 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<ShareDataRow>): 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']}`;
}
}
@@ -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';
@@ -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');
});
});
});
@@ -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<string>();
return lines.filter((line) => {
const lowerLine = line.toLowerCase();
if (seen.has(lowerLine)) {
return false;
}
seen.add(lowerLine);
return true;
});
}
}
@@ -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 {}
@@ -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';
@@ -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);
});
});
@@ -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`);
}
}
@@ -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';
@@ -79,6 +79,8 @@ export class SearchDateRangeComponent implements OnInit {
@Output()
valid = new EventEmitter<boolean>();
private readonly formBuilder = inject(FormBuilder);
form = this.formBuilder.group<SearchDateRange>({
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<DateFnsAdapter>,
@Inject(MAT_DATE_FORMATS) private dateFormatConfig: MatDateFormats
@@ -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<FileSizeCondition>({
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) {
-1
View File
@@ -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';
@@ -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();
}
@@ -39,7 +39,7 @@ import { MatError } from '@angular/material/form-field';
})
export class CardViewBoolItemComponent extends BaseCardView<CardViewBoolItemModel> {
@Input()
editable: boolean;
declare editable: boolean;
private _error: string;
@@ -28,7 +28,7 @@ export interface CardViewArrayItem {
export class CardViewArrayItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'array';
value: Observable<CardViewArrayItem[]>;
declare value: Observable<CardViewArrayItem[]>;
noOfItemsToDisplay: number;
constructor(cardViewArrayItemProperties: CardViewArrayItemProperties) {
@@ -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);
@@ -21,7 +21,7 @@ import { CardViewBaseItemModel } from './card-view-baseitem.model';
export class CardViewMapItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type = 'map';
value: Map<string, string>;
declare value: Map<string, string>;
get displayValue() {
if (this.value && this.value.size > 0) {
@@ -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;
@@ -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;
@@ -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
@@ -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);
@@ -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;
@@ -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);
@@ -260,7 +260,7 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessLi
processVariableFilters: ProcessVariableFilterModel[];
rows: TaskInstanceCloudListViewModel[] = [];
dataAdapter: TasksListDatatableAdapter | undefined;
declare dataAdapter: TasksListDatatableAdapter | undefined;
private isReloadingSubject$ = new BehaviorSubject<boolean>(false);
isLoading$ = combineLatest([this.isLoadingPreferences$, this.isReloadingSubject$]).pipe(
+1
View File
@@ -18,6 +18,7 @@
},
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"runtimeCacheInputs": ["node -v"]
}
+1 -1
View File
@@ -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",