[ACS-5645] Property Panel Feature (#3477)

* [ACS-5540] changes for edit aspect button

* added aspect edit button

* [ACS-5540]fixed unit test cases and added unit test cases

* [ACS-5540] Modified changes

* [ACS-5540] fixed file lock issue

* [ACS-5645]Implemented changes as per review comments

* [ACS-5540]Modified the test case title

* [ACS-5645] changes for aspect icon

* [ACS-5645] fixed aspect lock issue in small screen

* [ACS-5540] modified the aspect button changes

* [ACS-5540] modified the changes

* [ACS-5645] added unit test cases

* [ACS-5540] removed unwanted code

* [ACS-5540] fixed lock-file bug

* [ACS-5540] revert the unwanted changes

* [ACS-5540] modified changes

* [ACS-5540]Implemented the changes as per the review comments

* [ACS-5540] added group lock changes

* [ACS-5540] added tooltip

* [ACS-5540] Implemented the review comments

* [ACS-5540] added tooltips

* [ACS-5540] Added styles

* [ACS-5540]Added focus

* [ACS-5551]updated property panel design

* [ACS-5551]added null checks

* [ACS-5551] update style

* [ACS-5540] changes for edit aspect button

* added aspect edit button

* [ACS-5540]fixed unit test cases and added unit test cases

* [ACS-5540] Modified changes

* [ACS-5645]Implemented changes as per review comments

* [ACS-5645] changes for aspect icon

* [ACS-5540] modified the aspect button changes

* [ACS-5540] modified the changes

* [ACS-5540] revert the unwanted changes

* [ACS-5540] added group lock changes

* [ACS-5551]updated property panel design

* [ACS-5551]added null checks

* [ACS-5551] update style

* [ACS-5551] name updated

* [ACS-5551] unit test fix

* [ACS-5551] header issue fixed

* [ACS-5645] style updated

* [ACS-5645] border updated

* [ACS-6117] fixed aspect dispaly issue

* [ACS-5645] different node open issu fixed

* [ACS-5645] unit test issue fix

* [ACS-5645] unit test fix

* [ACS-5645] tabs design modify

* [ACS-5645] dependency updated

* [link-adf:ACS-564
5-property-panel-feature] test linking

* "[link-adf:ACS-5645-property-panel-feature]"

* [ACS-5645] revert adf linking changes

* add adf configs to libs

* fix issue with empty paths

* try using adf target

* [link-adf:ACS-5645-property-panel-feature] fix core mapping

* [link-adf:ACS-5645-property-panel-feature] revert target changes

* remove useless styles

* remove css hacks

* cleanup useless properties

* remove useless properties

* remove useless code

* [ACS-5645] added missing code

* [ACS-5654] add icon for full screen

* [ACS-5654] nodei con methods moved to thumbnail

* [ACS-5654] unit test added and code refactor

* [ACS-5645] unit test added

* [ACS-5645] panel issue fix

* [ACS-5645] removed unit test for editable property

* [ACS-5645] removed unused unit test

* [ACS-5645] unit test updated

* [ACS-5645] updated the unit test

* Modified the unit test cases for getNodeIcon

* Upsteam ADF-6.6.0-7287333895, Js-api-7.5.0-7287333895 version

* Fix failing test cases

* Fix failing e2e

---------

Co-authored-by: Yasa-Nataliya <yasa.nataliya@globallogic.com>
Co-authored-by: pkundu <priyanka.kundu@hyland.com>
Co-authored-by: Denys Vuika <denys.vuika@gmail.com>
Co-authored-by: rbahirsheth <raviraj.bahirsheth@globallogic.com>
This commit is contained in:
Anukriti Singh
2023-12-27 10:26:11 +05:30
committed by GitHub
parent 339dc60d6d
commit 90c69b207c
37 changed files with 418 additions and 247 deletions

View File

@@ -121,6 +121,28 @@ describe('app.evaluators', () => {
});
});
describe('canShowExpand', () => {
it('should return false when isLibraries returns true', () => {
const context: any = {
navigation: {
url: '/libraries'
}
};
expect(app.canShowExpand(context)).toBe(false);
});
it('should return false when isDetails returns true', () => {
const context: any = {
navigation: {
url: '/details'
}
};
expect(app.canShowExpand(context)).toBe(false);
});
});
describe('hasLockedFiles', () => {
it('should return [false] if selection not present', () => {
const context: any = {};
@@ -830,6 +852,42 @@ describe('app.evaluators', () => {
});
});
describe('editAspects', () => {
let context: TestRuleContext;
beforeEach(() => {
context = createTestContext();
});
it('should return true for multiselection', () => {
context.selection.count = 2;
expect(app.editAspects(context)).toBe(true);
});
it('should return false if user cannot update the selected node', () => {
context.permissions.check = spyOn(context.permissions, 'check').and.returnValue(false);
expect(app.editAspects(context)).toBe(false);
});
it('should return false if the selected node is write locked', () => {
context.selection.file = { entry: { properties: { 'cm:lockType': 'WRITE_LOCK' } } } as NodeEntry;
expect(app.editAspects(context)).toBe(false);
});
it('should return false if the context is trashcan', () => {
context.navigation = { url: '/trashcan' };
expect(app.editAspects(context)).toBe(false);
});
it('should return true if all conditions are met', () => {
expect(app.editAspects(context)).toBe(true);
});
});
describe('canManagePermissions', () => {
let context: TestRuleContext;

View File

@@ -498,6 +498,16 @@ export const canEditAspects = (context: RuleContext): boolean =>
repository.isMajorVersionAvailable(context, '7')
].every(Boolean);
export const editAspects = (context: RuleContext): boolean =>
[
canUpdateSelectedNode(context),
!isWriteLocked(context),
navigation.isNotTrashcan(context),
repository.isMajorVersionAvailable(context, '7')
].every(Boolean);
export const canShowExpand = (context: RuleContext): boolean => [!navigation.isLibraries(context), !navigation.isDetails(context)].every(Boolean);
/**
* Checks if user can manage permissions for the selected node.
* JSON ref: `canManagePermissions`

View File

@@ -225,6 +225,28 @@ describe('navigation.evaluators', () => {
});
});
describe('isDetails', () => {
it('should return true if url includes with `/details`', () => {
const context: any = {
navigation: {
url: '/details/path'
}
};
expect(app.isDetails(context)).toBe(true);
});
it('should return false if url not includes with `/details`', () => {
const context: any = {
navigation: {
url: '/path'
}
};
expect(app.isDetails(context)).toBe(false);
});
});
describe('isRecentFiles', () => {
it('should return [true] if url starts with `/recent-files`', () => {
const context: any = {

View File

@@ -110,6 +110,11 @@ export function isLibraryContent(context: RuleContext): boolean {
return url?.endsWith('/libraries') || url?.includes('/libraries/') || url?.startsWith('/search-libraries');
}
export function isDetails(context: RuleContext): boolean {
const { url } = context.navigation;
return url?.includes('/details');
}
/**
* Checks if the activated route is neither **Libraries** nor **Library Search Results**.
* JSON ref: `app.navigation.isNotLibraries`

View File

@@ -2,7 +2,7 @@
<mat-progress-bar mode="indeterminate" [attr.aria-label]="'APP.INFO_DRAWER.DATA_LOADING' | translate"></mat-progress-bar>
</div>
<ng-container *ngIf="!isLoading && !!displayNode">
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE'" cdkTrapFocusAutoCapture>
<adf-info-drawer [icon]="icon" [title]="node?.entry?.name || 'APP.INFO_DRAWER.TITLE'" cdkTrapFocusAutoCapture>
<aca-toolbar [items]="actions" info-drawer-buttons></aca-toolbar>
<adf-info-drawer-tab *ngFor="let tab of tabs" [icon]="tab.icon" [label]="tab.title">

View File

@@ -27,16 +27,19 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Store } from '@ngrx/store';
import { SetInfoDrawerStateAction, ToggleInfoDrawerAction } from '@alfresco/aca-shared/store';
import { of, Subject } from 'rxjs';
import { EMPTY, of, Subject } from 'rxjs';
import { InfoDrawerComponent } from './info-drawer.component';
import { LibTestingModule } from '../../testing/lib-testing-module';
import { AppExtensionService } from '../../services/app.extension.service';
import { ContentApiService } from '../../services/content-api.service';
import { ContentService } from '@alfresco/adf-content-services';
import { RedirectAuthService } from '@alfresco/adf-core';
describe('InfoDrawerComponent', () => {
let fixture: ComponentFixture<InfoDrawerComponent>;
let component: InfoDrawerComponent;
let contentApiService: ContentApiService;
let contentService: ContentService;
let tab: SidebarTabRef;
let appExtensionService: AppExtensionService;
const mockStream = new Subject();
@@ -62,7 +65,8 @@ describe('InfoDrawerComponent', () => {
imports: [LibTestingModule, InfoDrawerComponent],
providers: [
{ provide: AppExtensionService, useValue: extensionServiceMock },
{ provide: Store, useValue: storeMock }
{ provide: Store, useValue: storeMock },
{ provide: RedirectAuthService, useValue: { onLogin: EMPTY } }
],
schemas: [NO_ERRORS_SCHEMA]
});
@@ -71,7 +75,7 @@ describe('InfoDrawerComponent', () => {
component = fixture.componentInstance;
appExtensionService = TestBed.inject(AppExtensionService);
contentApiService = TestBed.inject(ContentApiService);
contentService = TestBed.inject(ContentService);
tab = { title: 'tab1', id: 'tab1', component: '' };
spyOn(appExtensionService, 'getSidebarTabs').and.returnValue([tab]);
});
@@ -140,6 +144,7 @@ describe('InfoDrawerComponent', () => {
it('should call getNodeInfo() when node is a recent file', () => {
const response: any = { entry: { id: 'nodeId' } };
spyOn(contentApiService, 'getNodeInfo').and.returnValue(of(response));
const nodeMock: any = {
entry: {
id: 'nodeId',
@@ -153,6 +158,7 @@ describe('InfoDrawerComponent', () => {
component.ngOnChanges();
expect(component.displayNode).toBe(response);
expect(component.node.entry).toBe(response);
expect(contentApiService.getNodeInfo).toHaveBeenCalled();
});
@@ -182,4 +188,23 @@ describe('InfoDrawerComponent', () => {
} as ContentActionRef
]);
});
it('should get node icon for documents', () => {
const expectedIcon = 'assets/images/ft_ic_folder';
const response: any = { entry: { id: 'nodeId' } };
spyOn(contentApiService, 'getNodeInfo').and.returnValue(of(response));
spyOn(contentService, 'getNodeIcon').and.returnValue(expectedIcon);
const nodeMock: any = {
entry: { id: 'nodeId', guid: 'guidId' },
isFolder: true
};
component.node = nodeMock;
fixture.detectChanges();
component.ngOnChanges();
expect(contentService.getNodeIcon).toHaveBeenCalledWith(response);
expect(component.icon).toBe(expectedIcon);
expect(contentApiService.getNodeInfo).toHaveBeenCalled();
});
});

View File

@@ -37,6 +37,7 @@ import { InfoDrawerModule } from '@alfresco/adf-core';
import { TranslateModule } from '@ngx-translate/core';
import { A11yModule } from '@angular/cdk/a11y';
import { ToolbarComponent } from '../toolbar/toolbar.component';
import { ContentService, NodesApiService } from '@alfresco/adf-content-services';
@Component({
standalone: true,
@@ -58,13 +59,20 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
actions: Array<ContentActionRef> = [];
onDestroy$ = new Subject<boolean>();
preventFromClosing = false;
icon: string = null;
@HostListener('keydown.escape')
onEscapeKeyboardEvent(): void {
this.close();
}
constructor(private store: Store<any>, private contentApi: ContentApiService, private extensions: AppExtensionService) {}
constructor(
private store: Store<any>,
private contentApi: ContentApiService,
private extensions: AppExtensionService,
private nodesService: NodesApiService,
private contentService: ContentService
) {}
ngOnInit() {
this.tabs = this.extensions.getSidebarTabs();
@@ -81,6 +89,10 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
.subscribe((isInfoDrawerPreviewOpened) => {
this.preventFromClosing = isInfoDrawerPreviewOpened;
});
this.nodesService.nodeUpdated.pipe(takeUntil(this.onDestroy$)).subscribe((node: any) => {
this.node.entry = node;
});
}
ngOnDestroy() {
@@ -115,6 +127,7 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
this.contentApi.getNodeInfo(nodeId).subscribe(
(entity) => {
this.setDisplayNode(entity);
this.node.entry = entity;
this.isLoading = false;
},
() => (this.isLoading = false)
@@ -124,5 +137,6 @@ export class InfoDrawerComponent implements OnChanges, OnInit, OnDestroy {
private setDisplayNode(node: any) {
this.displayNode = node;
this.icon = this.contentService.getNodeIcon(node);
}
}

View File

@@ -37,6 +37,8 @@
.aca-page-layout-content {
@include flex-row;
border-top: 1px solid var(--theme-metadata-property-panel-border-color);
}
.aca-page-layout-error {

View File

@@ -30,6 +30,7 @@ import { AppStore } from '@alfresco/aca-shared/store';
import {
ContentActionType,
ExtensionConfig,
ExtensionLoaderService,
ExtensionService,
filterEnabled,
mergeArrays,
@@ -53,6 +54,7 @@ describe('AppExtensionService', () => {
let logService: LogService;
let iconRegistry: MatIconRegistry;
let sanitizer: DomSanitizer;
let loader: ExtensionLoaderService;
beforeEach(() => {
TestBed.configureTestingModule({
@@ -70,6 +72,7 @@ describe('AppExtensionService', () => {
extensions = TestBed.inject(ExtensionService);
logService = TestBed.inject(LogService);
loader = TestBed.inject(ExtensionLoaderService);
});
const applyConfig = (config: ExtensionConfig, selection?: boolean) => {
@@ -1835,4 +1838,10 @@ describe('AppExtensionService', () => {
done();
});
});
it('should update sidebar actions correctly', () => {
spyOn(loader, 'getContentActions').and.callThrough();
service.updateSidebarActions();
expect(loader.getContentActions).toHaveBeenCalledWith(service.config, 'features.sidebar.toolbar');
});
});

View File

@@ -354,6 +354,10 @@ export class AppExtensionService implements RuleContext {
};
}
updateSidebarActions() {
this._sidebarActions.next(this.loader.getContentActions(this.config, 'features.sidebar.toolbar'));
}
getCreateActions(): Observable<Array<ContentActionRef>> {
return this._createActions.pipe(
map((createActions) =>