Angular 18 upgrade (#4448)

This commit is contained in:
dominikiwanekhyland
2025-05-15 14:39:45 +02:00
committed by GitHub
parent 4357fe9c08
commit 74e82c85a4
19 changed files with 6445 additions and 8032 deletions

View File

@@ -18,8 +18,8 @@
"zone.js": ">=0.11.8"
},
"dependencies": {
"tslib": "^2.3.0",
"ngx-markdown": "^17.2.1"
"tslib": ">=2.3.0",
"ngx-markdown": ">=17.2.1"
},
"publishConfig": {
"access": "public"

View File

@@ -29,7 +29,7 @@ import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, of, Subject } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppHookService, ContentApiService } from '@alfresco/aca-shared';
import { AppExtensionService, AppHookService, ContentApiService } from '@alfresco/aca-shared';
import { NavigateToFolder, SetSelectedNodesAction } from '@alfresco/aca-shared/store';
import { Node, NodeEntry, PathElement } from '@alfresco/js-api';
import { RouterTestingModule } from '@angular/router/testing';
@@ -57,14 +57,7 @@ describe('DetailsComponent', () => {
select: () => mockStream
};
const extensionsServiceMock = {
getAllowedSidebarActions: jasmine.createSpy('getAllowedSidebarActions')
};
const mockAspectActions: ContentActionRef[] = [];
const mockAspectActionsSubject$ = new BehaviorSubject(mockAspectActions);
extensionsServiceMock.getAllowedSidebarActions.and.returnValue(mockAspectActionsSubject$.asObservable());
const mockAspectActionsSubject$ = new BehaviorSubject<Array<ContentActionRef>>([]);
const getBreadcrumb = (): BreadcrumbComponent => fixture.debugElement.query(By.directive(BreadcrumbComponent)).componentInstance;
@@ -99,6 +92,9 @@ describe('DetailsComponent', () => {
schemas: [NO_ERRORS_SCHEMA]
});
const appExtensionService = TestBed.inject(AppExtensionService);
spyOn(appExtensionService, 'getAllowedSidebarActions').and.returnValue(mockAspectActionsSubject$.asObservable());
fixture = TestBed.createComponent(DetailsComponent);
component = fixture.componentInstance;
contentApiService = TestBed.inject(ContentApiService);
@@ -130,138 +126,133 @@ describe('DetailsComponent', () => {
fixture.destroy();
});
it('should get node id from router', () => {
fixture.detectChanges();
expect(component.nodeId).toBe('someId');
});
it('should set active tab from router', () => {
fixture.detectChanges();
expect(component.activeTab).toBe(2);
});
it('should get node info after setting node from router', () => {
fixture.detectChanges();
expect(contentApiService.getNode).toHaveBeenCalled();
});
it('should dispatch navigation to a given folder', () => {
const pathElement: PathElement = {
id: 'fake-id'
};
getBreadcrumb().navigate.emit(pathElement);
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledWith(new NavigateToFolder({ entry: pathElement } as NodeEntry));
});
it('should pass different node as folderNode to breadcrumb when nodeUpdated from nodesApiService is triggered', () => {
fixture.detectChanges();
const breadcrumbComponent = getBreadcrumb();
const updatedNode = {
name: 'other node'
} as Node;
nodesApiService.nodeUpdated.next(updatedNode);
fixture.detectChanges();
expect(breadcrumbComponent.folderNode).toEqual(updatedNode);
expect(breadcrumbComponent.folderNode).not.toBe(updatedNode);
expect(updatedNode).not.toEqual(node.entry);
});
it('should dispatch node selection', () => {
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledWith(new SetSelectedNodesAction([node]));
});
it('should set aspectActions from extensions', async () => {
extensionsServiceMock.getAllowedSidebarActions.and.returnValue(of(mockAspectActions));
fixture.detectChanges();
await fixture.whenStable().then(() => {
expect(component.aspectActions).toEqual(mockAspectActions);
});
});
it('should return the icon when getNodeIcon is called', () => {
const expectedIcon = 'assets/images/ft_ic_folder';
spyOn(contentService, 'getNodeIcon').and.returnValue(expectedIcon);
fixture.detectChanges();
component.ngOnInit();
expect(contentService.getNodeIcon).toHaveBeenCalled();
expect(component.nodeIcon).toContain(expectedIcon);
});
it('should set aspectActions from extension mock', () => {
const extensionMock = {
getAllowedSidebarActions: () =>
of([
{
id: 'app.sidebar.close',
order: 100,
title: 'close',
icon: 'highlight_off'
}
])
};
const extensionMock = [
{
id: 'app.sidebar.close',
order: 100,
title: 'close',
icon: 'highlight_off'
} as ContentActionRef
];
mockAspectActionsSubject$.next(extensionMock);
extensionsServiceMock.getAllowedSidebarActions.and.returnValue(of(extensionMock));
fixture.detectChanges();
fixture
.whenStable()
.then(() => {
expect(component.aspectActions).toEqual([
{
id: 'app.sidebar.close',
order: 100,
title: 'close',
icon: 'highlight_off'
} as ContentActionRef
]);
})
.catch((error) => {
fail(`An error occurred: ${error}`);
expect(component.aspectActions).toEqual([
{
id: 'app.sidebar.close',
order: 100,
title: 'close',
icon: 'highlight_off'
} as ContentActionRef
]);
});
describe('', () => {
beforeEach(() => {
mockAspectActionsSubject$.next([]);
});
it('should get node id from router', () => {
fixture.detectChanges();
expect(component.nodeId).toBe('someId');
});
it('should set active tab from router', () => {
fixture.detectChanges();
expect(component.activeTab).toBe(2);
});
it('should get node info after setting node from router', () => {
fixture.detectChanges();
expect(contentApiService.getNode).toHaveBeenCalled();
});
it('should dispatch navigation to a given folder', () => {
const pathElement: PathElement = {
id: 'fake-id'
};
getBreadcrumb().navigate.emit(pathElement);
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledWith(new NavigateToFolder({ entry: pathElement } as NodeEntry));
});
it('should pass different node as folderNode to breadcrumb when nodeUpdated from nodesApiService is triggered', () => {
fixture.detectChanges();
const breadcrumbComponent = getBreadcrumb();
const updatedNode = {
name: 'other node'
} as Node;
nodesApiService.nodeUpdated.next(updatedNode);
fixture.detectChanges();
expect(breadcrumbComponent.folderNode).toEqual(updatedNode);
expect(breadcrumbComponent.folderNode).not.toBe(updatedNode);
expect(updatedNode).not.toEqual(node.entry);
});
it('should dispatch node selection', () => {
fixture.detectChanges();
expect(store.dispatch).toHaveBeenCalledWith(new SetSelectedNodesAction([node]));
});
it('should set aspectActions from extensions', async () => {
fixture.detectChanges();
await fixture.whenStable().then(() => {
expect(component.aspectActions).toEqual([]);
});
});
});
it('should disable the permissions tab for smart folders based on aspects', () => {
node.entry.isFolder = true;
node.entry.aspectNames = ['smf:customConfigSmartFolder'];
fixture.detectChanges();
component.ngOnInit();
expect(component.canManagePermissions).toBeFalse();
expect(component.activeTab).not.toBe(2);
});
it('should return the icon when getNodeIcon is called', () => {
const expectedIcon = 'assets/images/ft_ic_folder';
spyOn(contentService, 'getNodeIcon').and.returnValue(expectedIcon);
fixture.detectChanges();
component.ngOnInit();
expect(contentService.getNodeIcon).toHaveBeenCalled();
expect(component.nodeIcon).toContain(expectedIcon);
});
it('should enable the permissions tab for regular folders based on aspects', () => {
node.entry.isFolder = true;
node.entry.aspectNames = [];
fixture.detectChanges();
component.ngOnInit();
it('should disable the permissions tab for smart folders based on aspects', () => {
node.entry.isFolder = true;
node.entry.aspectNames = ['smf:customConfigSmartFolder'];
fixture.detectChanges();
component.ngOnInit();
expect(component.canManagePermissions).toBeFalse();
expect(component.activeTab).not.toBe(2);
});
expect(component.canManagePermissions).toBeTrue();
});
it('should enable the permissions tab for regular folders based on aspects', () => {
node.entry.isFolder = true;
node.entry.aspectNames = [];
fixture.detectChanges();
component.ngOnInit();
it('should change active tab based on canManagePermissions and tabName', () => {
component.nodeId = 'someNodeId';
component.activeTab = 0;
expect(component.canManagePermissions).toBeTrue();
});
node.entry.isFolder = true;
node.entry.aspectNames = [];
it('should change active tab based on canManagePermissions and tabName', () => {
component.nodeId = 'someNodeId';
component.activeTab = 0;
fixture.detectChanges();
component.ngOnInit();
node.entry.isFolder = true;
node.entry.aspectNames = [];
component.setActiveTab('permissions');
expect(component.activeTab).toBe(2);
fixture.detectChanges();
component.ngOnInit();
node.entry.isFolder = true;
node.entry.aspectNames = ['smf:customConfigSmartFolder'];
component.setActiveTab('permissions');
expect(component.activeTab).toBe(2);
fixture.detectChanges();
component.ngOnInit();
node.entry.isFolder = true;
node.entry.aspectNames = ['smf:customConfigSmartFolder'];
component.setActiveTab('permissions');
expect(component.activeTab).not.toBe(2);
fixture.detectChanges();
component.ngOnInit();
component.setActiveTab('permissions');
expect(component.activeTab).not.toBe(2);
});
});
it('should navigate back when nodesDeleted event is triggered', () => {

View File

@@ -22,13 +22,13 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { AppTestingModule } from '../../testing/app-testing.module';
import { ViewerEffects } from './viewer.effects';
import { EffectsModule } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { Router } from '@angular/router';
import { ViewFileAction, ViewNodeAction, SetSelectedNodesAction, SetCurrentFolderAction } from '@alfresco/aca-shared/store';
import { SetCurrentFolderAction, SetSelectedNodesAction, ViewFileAction, ViewNodeAction } from '@alfresco/aca-shared/store';
import { MatDialogModule } from '@angular/material/dialog';
describe('ViewerEffects', () => {
@@ -58,6 +58,7 @@ describe('ViewerEffects', () => {
tick(100);
expect(router.navigateByUrl).toHaveBeenCalledWith('/folder1/preview/someId');
}));
it('should preview file from payload', fakeAsync(() => {
const node: any = { entry: { isFile: true, id: 'someId' } };
store.dispatch(new ViewFileAction(node));

View File

@@ -11,7 +11,7 @@ body {
font-size: 14px;
font-family: 'Open Sans', serif;
color: mat.get-color-from-palette($foreground, text, 0.87);
color: mat.m2-get-color-from-palette($foreground, text, 0.87);
margin: 0;
& > main {

View File

@@ -6,25 +6,25 @@
@import './overrides/adf-about.theme';
@import './colors';
$mat-primary-palette: mat.define-palette($aca-primary-blue, A100);
$mat-accent-palette: mat.define-palette($aca-accent-green, A200);
$mat-warn-palette: mat.define-palette($aca-warn, A100);
$app-typography: mat.define-typography-config(
$mat-primary-palette: mat.m2-define-palette($aca-primary-blue, A100);
$mat-accent-palette: mat.m2-define-palette($aca-accent-green, A200);
$mat-warn-palette: mat.m2-define-palette($aca-warn, A100);
$app-typography: mat.m2-define-typography-config(
$font-family: 'Open Sans',
$headline-1: mat.define-typography-level(112px, 112px, 300),
$headline-2: mat.define-typography-level(56px, 56px, 400),
$headline-3: mat.define-typography-level(45px, 48px, 400),
$headline-4: mat.define-typography-level(34px, 40px, 400),
$headline-5: mat.define-typography-level(24px, 32px, 400),
$headline-6: mat.define-typography-level(20px, 32px, 400),
$subtitle-1: mat.define-typography-level(16px, 28px, 400),
$subtitle-2: mat.define-typography-level(14px, 24px, 500),
$body-2: mat.define-typography-level(14px, 24px, 500),
$body-1: mat.define-typography-level(14px, 20px, 400),
$caption: mat.define-typography-level(12px, 20px, 400),
$button: mat.define-typography-level(14px, 14px, 500)
$headline-1: mat.m2-define-typography-level(112px, 112px, 300),
$headline-2: mat.m2-define-typography-level(56px, 56px, 400),
$headline-3: mat.m2-define-typography-level(45px, 48px, 400),
$headline-4: mat.m2-define-typography-level(34px, 40px, 400),
$headline-5: mat.m2-define-typography-level(24px, 32px, 400),
$headline-6: mat.m2-define-typography-level(20px, 32px, 400),
$subtitle-1: mat.m2-define-typography-level(16px, 28px, 400),
$subtitle-2: mat.m2-define-typography-level(14px, 24px, 500),
$body-2: mat.m2-define-typography-level(14px, 24px, 500),
$body-1: mat.m2-define-typography-level(14px, 20px, 400),
$caption: mat.m2-define-typography-level(12px, 20px, 400),
$button: mat.m2-define-typography-level(14px, 14px, 500)
);
$custom-theme: mat.define-light-theme(
$custom-theme: mat.m2-define-light-theme(
(
color: (
primary: $mat-primary-palette,

View File

@@ -117,11 +117,14 @@
}
.mat-mdc-slide-toggle .mdc-form-field {
.mdc-switch .mdc-switch__handle {
width: 15px;
height: 15px;
transform: translate(20%, 50%);
background-color: #484a4d;
button.mdc-switch.mdc-switch--selected,
button.mdc-switch.mdc-switch--unselected {
.mdc-switch__handle {
width: 15px;
height: 15px;
transform: translate(20%, 50%);
background-color: #484a4d;
}
}
.mdc-switch .mdc-switch__track {

View File

@@ -11,6 +11,10 @@
--mdc-text-button-label-text-color: inherit;
--mat-toolbar-container-text-color: inherit;
--mat-form-field-container-height: unset;
--mdc-checkbox-selected-icon-color: var(--theme-blue-checkbox-color);
--mdc-checkbox-selected-hover-icon-color: var(--theme-blue-checkbox-color);
--mdc-checkbox-selected-focus-icon-color: var(--theme-blue-checkbox-color);
--mat-dialog-container-max-width: 100%;
}
mat-toolbar {
@@ -66,14 +70,6 @@ mat-toolbar {
height: 24px;
width: 24px;
}
#{ms.$mat-checkbox-box}:has(#{ms.$mat-checkbox-native-control}):hover,
#{ms.$mat-checkbox-box}:has(#{ms.$mat-checkbox-native-control}) {
#{ms.$mat-checkbox-native-control}:enabled ~ #{ms.$mat-checkbox-background}:is(div) {
border-color: var(--theme-blue-checkbox-color);
background-color: var(--theme-blue-checkbox-color);
}
}
}
#{ms.$mat-switch}:is(button)#{ms.$mat-switch-selected}#{ms.$mat-switch-checked} #{ms.$mat-switch-handle-track} #{ms.$mat-switch-handle}::after {
@@ -352,3 +348,10 @@ adf-dynamic-component {
#{ms.$mat-form-field-infix} {
min-height: unset;
}
#{ms.$mat-mdc-submenu-icon} {
position: absolute;
top: 50%;
right: 6px;
transform: translateY(-50%);
}

View File

@@ -4,10 +4,10 @@
$foreground: map-get($custom-theme, foreground);
$background: map-get($custom-theme, background);
$background-color: mat.get-color-from-palette($background, background);
$background-card-color: mat.get-color-from-palette($background, card);
$text-color: mat.get-color-from-palette($foreground, text);
$secondary-text: mat.get-color-from-palette($foreground, secondary-text);
$background-color: mat.m2-get-color-from-palette($background, background);
$background-card-color: mat.m2-get-color-from-palette($background, card);
$text-color: mat.m2-get-color-from-palette($foreground, text);
$secondary-text: mat.m2-get-color-from-palette($foreground, secondary-text);
// Custom variables - ACA specific styling:
$data-table-thumbnail-width: 35px;
@@ -44,7 +44,7 @@ $action-button-text-color: lighten($text-color, 35%);
$page-layout-header-background-color: $background-card-color;
$search-chip-icon-color: #757575;
$disabled-chip-background-color: #f5f5f5;
$contrast-gray: mat.get-color-from-palette($foreground, 'secondary-tex');
$contrast-gray: mat.m2-get-color-from-palette($foreground, 'secondary-tex');
$search-highlight-background-color: #ffd180;
$info-snackbar-background: #1f74db;
$text-light-color: rgba(33, 35, 40, 0.7);
@@ -57,22 +57,22 @@ $light-grey-3: #dedede;
$defaults: (
--theme-background-color: $background-color,
--theme-search-background-color: darken($background-color, 1),
--theme-text-color: mat.get-color-from-palette($foreground, text, 0.54),
--theme-sidenav-text-color: mat.get-color-from-palette($foreground, text),
--theme-sidenav-hovered-text-color: mat.get-color-from-palette($foreground, text),
--theme-sidenav-user-menu-color: mat.get-color-from-palette($foreground, text),
--theme-tab-active-text-color: mat.get-color-from-palette($foreground, text),
--theme-text-bold-color: mat.get-color-from-palette($foreground, text, 0.87),
--theme-title-color: mat.get-color-from-palette($foreground, text, 0.87),
--theme-text-disabled-color: mat.get-color-from-palette($foreground, text, 0.38),
--theme-border-color: mat.get-color-from-palette($foreground, text, 0.07),
--theme-card-background-color: mat.get-color-from-palette($background, card),
--theme-foreground-text-color: mat.get-color-from-palette($foreground, text, 0.72),
--theme-foreground-text-bold-color: mat.get-color-from-palette($foreground, text, 0.87),
--theme-secondary-text-color: mat.get-color-from-palette($foreground, secondary-text),
--theme-divider-color: mat.get-color-from-palette($foreground, divider, 0.07),
--theme-dialog-background-color: mat.get-color-from-palette($background, dialog),
--theme-header-text-color: mat.get-color-from-palette($foreground, text, 0.87),
--theme-text-color: mat.m2-get-color-from-palette($foreground, text, 0.54),
--theme-sidenav-text-color: mat.m2-get-color-from-palette($foreground, text),
--theme-sidenav-hovered-text-color: mat.m2-get-color-from-palette($foreground, text),
--theme-sidenav-user-menu-color: mat.m2-get-color-from-palette($foreground, text),
--theme-tab-active-text-color: mat.m2-get-color-from-palette($foreground, text),
--theme-text-bold-color: mat.m2-get-color-from-palette($foreground, text, 0.87),
--theme-title-color: mat.m2-get-color-from-palette($foreground, text, 0.87),
--theme-text-disabled-color: mat.m2-get-color-from-palette($foreground, text, 0.38),
--theme-border-color: mat.m2-get-color-from-palette($foreground, text, 0.07),
--theme-card-background-color: mat.m2-get-color-from-palette($background, card),
--theme-foreground-text-color: mat.m2-get-color-from-palette($foreground, text, 0.72),
--theme-foreground-text-bold-color: mat.m2-get-color-from-palette($foreground, text, 0.87),
--theme-secondary-text-color: mat.m2-get-color-from-palette($foreground, secondary-text),
--theme-divider-color: mat.m2-get-color-from-palette($foreground, divider, 0.07),
--theme-dialog-background-color: mat.m2-get-color-from-palette($background, dialog),
--theme-header-text-color: mat.m2-get-color-from-palette($foreground, text, 0.87),
--new-button-font-size: 0.9rem,
--theme-grey-text-background-color: $grey-text-background,
--theme-grey-background-color: $grey-background,
@@ -86,10 +86,10 @@ $defaults: (
--theme-dropdown-color: $theme-dropdown-background,
--theme-dropdown-background-hover: $theme-dropdown-background-hover,
--theme-grey-divider-color: $grey-divider,
--theme-pagination-background-color: mat.get-color-from-palette($background, background),
--theme-pagination-background-color: mat.m2-get-color-from-palette($background, background),
--theme-about-panel-border-color: $grey-background,
--theme-about-panel-background-color: mat.get-color-from-palette($background, card),
--theme-about-panel-title-color: mat.get-color-from-palette($foreground, text),
--theme-about-panel-background-color: mat.m2-get-color-from-palette($background, card),
--theme-about-panel-title-color: mat.m2-get-color-from-palette($foreground, text),
--theme-datetimepicker-font-color: $datetimepicker-font-color,
--theme-datetimepicker-selected-date-background: $datetimepicker-selected-date-background,
--theme-datetimepicker-cell-background: $datetimepicker-cell-background-color,

View File

@@ -22,20 +22,20 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { Router, ActivatedRoute } from '@angular/router';
import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { AuthenticationService } from '@alfresco/adf-core';
import { UploadService, NodesApiService, DiscoveryApiService, DocumentListService } from '@alfresco/adf-content-services';
import { DiscoveryApiService, DocumentListService, NodesApiService, UploadService } from '@alfresco/adf-content-services';
import { ClosePreviewAction, RefreshPreviewAction, ViewNodeAction } from '@alfresco/aca-shared/store';
import { AcaViewerComponent } from './viewer.component';
import { of } from 'rxjs';
import {
ContentApiService,
AppHookService,
DocumentBasePageService,
LibTestingModule,
ContentApiService,
discoveryApiServiceMockValue,
DocumentBasePageServiceMock
DocumentBasePageService,
DocumentBasePageServiceMock,
LibTestingModule
} from '@alfresco/aca-shared';
import { Store } from '@ngrx/store';
import { Node } from '@alfresco/js-api';
@@ -69,7 +69,7 @@ describe('AcaViewerComponent', () => {
TestBed.configureTestingModule({
imports: [LibTestingModule, AcaViewerComponent],
providers: [
{ provide: DocumentBasePageService, useValue: DocumentBasePageServiceMock },
{ provide: DocumentBasePageService, useClass: DocumentBasePageServiceMock },
{ provide: DiscoveryApiService, useValue: discoveryApiServiceMockValue },
{ provide: AuthenticationService, useValue: {} }
]

View File

@@ -71,7 +71,7 @@ export class AdfInfoDrawerComponent extends BaseComponent {
public tagsAccordionPenButton = this.tagsAccordion.locator('[data-automation-id="showing-tag-input-button"]');
public categoriesAccordionPenButton = this.categoriesAccordion.locator('[data-automation-id="meta-data-categories-edit"]');
public tagsInput = this.tagsCreator.locator('input');
public createTagButton = this.tagsCreator.locator('[role="button"]');
public createTagButton = this.tagsCreator.locator('.adf-create-tag-label');
public tagsChips = this.tagsCreator.locator('mat-chip');
public tagsChipsXButton = this.tagsChips.locator('.adf-dynamic-chip-list-delete-icon');
public tagsAccordionCancelButton = this.getChild('[data-automation-id="reset-tags-metadata"]');
@@ -79,7 +79,7 @@ export class AdfInfoDrawerComponent extends BaseComponent {
public categoriesAccordionCancelButton = this.getChild('[data-automation-id="reset-metadata"]');
public categoriesAccordionConfirmButton = this.getChild('[data-automation-id="save-categories-metadata"]');
public categoriesInput = this.categoriesManagement.locator('input');
public categoriesListItems = this.categoriesManagement.locator('mat-list-item');
public categoriesListItems = this.categoriesManagement.locator('.adf-category');
public categoriesItemRemoveButton = this.categoriesManagement.locator('[data-automation-id="categories-remove-category-button"]');
public categoriesCreatedList = this.getChild('.adf-metadata-categories');