mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[AAE-3115] Content node selector - Ability to select multiple files (#5904)
* Ability to select multiple files * Fix unit test * Rename event into NodeSelected * restrict the typo Co-authored-by: Denys Vuika <denys.vuika@alfresco.com> Co-authored-by: Denys Vuika <denys.vuika@alfresco.com>
This commit is contained in:
parent
5a820cbecd
commit
1dde6bb1c5
@ -132,7 +132,7 @@ registerLocaleData(localeSv);
|
|||||||
ContentModule.forRoot(),
|
ContentModule.forRoot(),
|
||||||
InsightsModule.forRoot(),
|
InsightsModule.forRoot(),
|
||||||
ProcessModule.forRoot(),
|
ProcessModule.forRoot(),
|
||||||
ProcessServicesCloudModule,
|
ProcessServicesCloudModule.forRoot(),
|
||||||
ExtensionsModule.forRoot(),
|
ExtensionsModule.forRoot(),
|
||||||
ThemePickerModule,
|
ThemePickerModule,
|
||||||
ChartsModule,
|
ChartsModule,
|
||||||
|
@ -17,13 +17,17 @@
|
|||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { NotificationService, AppConfigService } from '@alfresco/adf-core';
|
import { NotificationService, AppConfigService, FormRenderingService } from '@alfresco/adf-core';
|
||||||
import { CloudLayoutService } from './services/cloud-layout.service';
|
import { CloudLayoutService } from './services/cloud-layout.service';
|
||||||
import { PreviewService } from '../../services/preview.service';
|
import { PreviewService } from '../../services/preview.service';
|
||||||
|
import { CloudFormRenderingService } from '@alfresco/adf-process-services-cloud';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './start-process-cloud-demo.component.html',
|
templateUrl: './start-process-cloud-demo.component.html',
|
||||||
styleUrls: ['./start-process-cloud-demo.component.scss']
|
styleUrls: ['./start-process-cloud-demo.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{ provide: FormRenderingService, useClass: CloudFormRenderingService }
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class StartProcessCloudDemoComponent implements OnInit {
|
export class StartProcessCloudDemoComponent implements OnInit {
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ Opens a [Content Node Selector](content-node-selector.component.md) in its own
|
|||||||
| showDropdownSiteList | `boolean` | | Toggle sites list dropdown rendering |
|
| showDropdownSiteList | `boolean` | | Toggle sites list dropdown rendering |
|
||||||
| showFilesInResult | `void` | | Shows the files and folders in the search result |
|
| showFilesInResult | `void` | | Shows the files and folders in the search result |
|
||||||
| showSearch | `boolean` | | Toggle search input rendering |
|
| showSearch | `boolean` | | Toggle search input rendering |
|
||||||
|
| selectionMode | `string` | "single" | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode, you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
|
@ -94,6 +94,7 @@ Displays the documents from a repository.
|
|||||||
| nodeDblClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user double-clicks a list node |
|
| nodeDblClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user double-clicks a list node |
|
||||||
| preview | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user acts upon files with either single or double click (depends on `navigation-mode`). Useful for integration with the [Viewer component](../../core/components/viewer.component.md). |
|
| preview | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntityEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Emitted when the user acts upon files with either single or double click (depends on `navigation-mode`). Useful for integration with the [Viewer component](../../core/components/viewer.component.md). |
|
||||||
| ready | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/NodePaging.md)`>` | Emitted when the Document List has loaded all items and is ready for use |
|
| ready | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodePaging`](https://github.com/Alfresco/alfresco-js-api/blob/development/src/api/content-rest-api/docs/NodePaging.md)`>` | Emitted when the Document List has loaded all items and is ready for use |
|
||||||
|
| nodeSelected | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`NodeEntryEvent`](../../../lib/content-services/src/lib/document-list/components/node.event.ts)`>` | Returns the current node selected |
|
||||||
|
|
||||||
## Details
|
## Details
|
||||||
|
|
||||||
|
@ -140,6 +140,7 @@ export class ContentNodeDialogService {
|
|||||||
const data: ContentNodeSelectorComponentData = {
|
const data: ContentNodeSelectorComponentData = {
|
||||||
title: this.getTitleTranslation(action, contentEntry.name),
|
title: this.getTitleTranslation(action, contentEntry.name),
|
||||||
actionName: action,
|
actionName: action,
|
||||||
|
selectionMode: 'single',
|
||||||
currentFolderId: contentEntry.parentId,
|
currentFolderId: contentEntry.parentId,
|
||||||
imageResolver: this.imageResolver.bind(this),
|
imageResolver: this.imageResolver.bind(this),
|
||||||
where: '(isFolder=true)',
|
where: '(isFolder=true)',
|
||||||
@ -182,6 +183,7 @@ export class ContentNodeDialogService {
|
|||||||
const data: ContentNodeSelectorComponentData = {
|
const data: ContentNodeSelectorComponentData = {
|
||||||
title: this.getTitleTranslation(action, this.translation.instant('DROPDOWN.MY_FILES_OPTION')),
|
title: this.getTitleTranslation(action, this.translation.instant('DROPDOWN.MY_FILES_OPTION')),
|
||||||
actionName: action,
|
actionName: action,
|
||||||
|
selectionMode: 'single',
|
||||||
currentFolderId: contentEntry.id,
|
currentFolderId: contentEntry.id,
|
||||||
imageResolver: this.imageResolver.bind(this),
|
imageResolver: this.imageResolver.bind(this),
|
||||||
isSelectionValid: this.hasAllowableOperationsOnNodeFolder.bind(this),
|
isSelectionValid: this.hasAllowableOperationsOnNodeFolder.bind(this),
|
||||||
@ -209,6 +211,7 @@ export class ContentNodeDialogService {
|
|||||||
const data: ContentNodeSelectorComponentData = {
|
const data: ContentNodeSelectorComponentData = {
|
||||||
title: this.getTitleTranslation(action, this.translation.instant('DROPDOWN.MY_FILES_OPTION')),
|
title: this.getTitleTranslation(action, this.translation.instant('DROPDOWN.MY_FILES_OPTION')),
|
||||||
actionName: action,
|
actionName: action,
|
||||||
|
selectionMode: 'single',
|
||||||
currentFolderId: contentEntry.id,
|
currentFolderId: contentEntry.id,
|
||||||
imageResolver: this.imageResolver.bind(this),
|
imageResolver: this.imageResolver.bind(this),
|
||||||
isSelectionValid: (entry: Node) => entry.isFile,
|
isSelectionValid: (entry: Node) => entry.isFile,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="adf-content-node-selector-content" (node-select)="onNodeSelect($event)">
|
<div class="adf-content-node-selector-content">
|
||||||
<mat-form-field floatPlaceholder="never" class="adf-content-node-selector-content-input" *ngIf="showSearch">
|
<mat-form-field floatPlaceholder="never" class="adf-content-node-selector-content-input" *ngIf="showSearch">
|
||||||
<input matInput
|
<input matInput
|
||||||
id="searchInput"
|
id="searchInput"
|
||||||
@ -64,7 +64,7 @@
|
|||||||
[rowFilter]="_rowFilter"
|
[rowFilter]="_rowFilter"
|
||||||
[imageResolver]="imageResolver"
|
[imageResolver]="imageResolver"
|
||||||
[currentFolderId]="folderIdToShow"
|
[currentFolderId]="folderIdToShow"
|
||||||
selectionMode="single"
|
[selectionMode]="selectionMode"
|
||||||
[contextMenuActions]="false"
|
[contextMenuActions]="false"
|
||||||
[contentActions]="false"
|
[contentActions]="false"
|
||||||
[allowDropFiles]="false"
|
[allowDropFiles]="false"
|
||||||
@ -72,6 +72,7 @@
|
|||||||
[where]="where"
|
[where]="where"
|
||||||
(folderChange)="onFolderChange()"
|
(folderChange)="onFolderChange()"
|
||||||
(ready)="onFolderLoaded()"
|
(ready)="onFolderLoaded()"
|
||||||
|
(nodeSelected)="onCurrentSelection($event)"
|
||||||
data-automation-id="content-node-selector-document-list">
|
data-automation-id="content-node-selector-document-list">
|
||||||
|
|
||||||
<adf-custom-empty-content-template>
|
<adf-custom-empty-content-template>
|
||||||
|
@ -133,14 +133,14 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger the select event when selection has been made', (done) => {
|
it('should trigger the select event when selection has been made', (done) => {
|
||||||
const expectedNode = <Node> {};
|
const expectedNode = <Node> { id: 'fakeid'};
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes.length).toBe(1);
|
expect(nodes.length).toBe(1);
|
||||||
expect(nodes[0]).toBe(expectedNode);
|
expect(nodes[0]).toBe(expectedNode);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.chosenNode = expectedNode;
|
component.chosenNode = [expectedNode];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to filter out the exclude site content', () => {
|
it('should be able to filter out the exclude site content', () => {
|
||||||
@ -291,7 +291,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
|
|
||||||
const chosenNode = new Node({ path: { elements: ['one'] } });
|
const chosenNode = new Node({ path: { elements: ['one'] } });
|
||||||
component.onNodeSelect({ detail: { node: { entry: chosenNode } } });
|
component.onCurrentSelection([ { entry: chosenNode } ]);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
@ -313,7 +313,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const chosenNode = <Node> { path: { elements: [] } };
|
const chosenNode = <Node> { path: { elements: [] } };
|
||||||
component.onNodeSelect({ detail: { node: { entry: chosenNode } } });
|
component.onCurrentSelection([ { entry: chosenNode } ]);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
@ -395,6 +395,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
|
|
||||||
describe('Search functionality', () => {
|
describe('Search functionality', () => {
|
||||||
let getCorrespondingNodeIdsSpy;
|
let getCorrespondingNodeIdsSpy;
|
||||||
|
const entry: Node = <Node> { id: 'fakeid'};
|
||||||
|
|
||||||
const defaultSearchOptions = (searchTerm, rootNodeId = undefined, skipCount = 0, showFiles = false) => {
|
const defaultSearchOptions = (searchTerm, rootNodeId = undefined, skipCount = 0, showFiles = false) => {
|
||||||
|
|
||||||
@ -472,7 +473,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should reset the currently chosen node in case of starting a new search', fakeAsync(() => {
|
it('should reset the currently chosen node in case of starting a new search', fakeAsync(() => {
|
||||||
component.chosenNode = <Node> {};
|
component.chosenNode = [entry];
|
||||||
typeToSearchBox('kakarot');
|
typeToSearchBox('kakarot');
|
||||||
|
|
||||||
tick(debounceSearch);
|
tick(debounceSearch);
|
||||||
@ -617,10 +618,11 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => {
|
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => {
|
||||||
component.chosenNode = <Node> {};
|
component.chosenNode = [entry];
|
||||||
|
|
||||||
component.nodePaging = {
|
component.nodePaging = {
|
||||||
list: {
|
list: {
|
||||||
entries: [{ entry: component.chosenNode }]
|
entries: [{ entry }]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
component.searchTerm = 'piccolo';
|
component.searchTerm = 'piccolo';
|
||||||
@ -935,7 +937,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
|
|
||||||
describe('Chosen node', () => {
|
describe('Chosen node', () => {
|
||||||
|
|
||||||
const entry: Node = <Node> {};
|
const entry: Node = <Node> { id: 'fakeid'};
|
||||||
const nodePage: NodePaging = <NodePaging> { list: {}, pagination: {} };
|
const nodePage: NodePaging = <NodePaging> { list: {}, pagination: {} };
|
||||||
let hasAllowableOperations;
|
let hasAllowableOperations;
|
||||||
|
|
||||||
@ -960,11 +962,10 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).not.toBeNull();
|
expect(nodes).not.toBeNull();
|
||||||
expect(component.chosenNode).toBe(entry);
|
expect(component.chosenNode[0]).toBe(entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
component.documentList.ready.emit(nodePage);
|
component.documentList.ready.emit(nodePage);
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should be null after selecting node without the necessary permissions', async(() => {
|
it('should be null after selecting node without the necessary permissions', async(() => {
|
||||||
@ -978,7 +979,6 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
component.documentList.ready.emit(nodePage);
|
component.documentList.ready.emit(nodePage);
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should NOT be null after clicking on a node (with the right permissions) in the list (onNodeSelect)', async(() => {
|
it('should NOT be null after clicking on a node (with the right permissions) in the list (onNodeSelect)', async(() => {
|
||||||
@ -987,54 +987,48 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).not.toBeNull();
|
expect(nodes).not.toBeNull();
|
||||||
expect(component.chosenNode).toBe(entry);
|
expect(component.chosenNode[0]).toBe(entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
component.onNodeSelect({ detail: { node: { entry } } });
|
component.onCurrentSelection([ { entry } ]);
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should remain null when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', async(() => {
|
it('should remain empty when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', async(() => {
|
||||||
hasAllowableOperations = false;
|
hasAllowableOperations = false;
|
||||||
|
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).toBeNull();
|
expect(nodes).toEqual([]);
|
||||||
expect(component.chosenNode).toBeNull();
|
expect(component.chosenNode).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
component.onNodeSelect({ detail: { node: { entry } } });
|
component.onCurrentSelection([ { entry } ]);
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should become null when clicking on a node (with the WRONG permissions) after previously selecting a right node', async(() => {
|
it('should become empty when clicking on a node (with the WRONG permissions) after previously selecting a right node', async(() => {
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
|
|
||||||
if (hasAllowableOperations) {
|
if (hasAllowableOperations) {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).not.toBeNull();
|
expect(nodes).not.toBeNull();
|
||||||
expect(component.chosenNode).not.toBeNull();
|
expect(component.chosenNode[0]).not.toBeNull();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).toBeNull();
|
expect(nodes).toEqual([]);
|
||||||
expect(component.chosenNode).toBeNull();
|
expect(component.chosenNode).toEqual([]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
hasAllowableOperations = true;
|
hasAllowableOperations = true;
|
||||||
component.onNodeSelect({ detail: { node: { entry } } });
|
component.onCurrentSelection([{ entry }]);
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
hasAllowableOperations = false;
|
hasAllowableOperations = false;
|
||||||
component.onNodeSelect({ detail: { node: { entry } } });
|
component.onCurrentSelection([{ entry }]);
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should be null when the chosenNode is reset', async(() => {
|
it('should be empty when the chosenNode is reset', async(() => {
|
||||||
hasAllowableOperations = true;
|
hasAllowableOperations = true;
|
||||||
component.onNodeSelect({ detail: { node: { entry: <Node> {} } } });
|
component.onCurrentSelection([{ entry: <Node> {} }]);
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
@ -1043,7 +1037,6 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
component.resetChosenNode();
|
component.resetChosenNode();
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1060,7 +1053,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).not.toBeNull();
|
expect(nodes).not.toBeNull();
|
||||||
expect(component.chosenNode).not.toBeNull();
|
expect(component.chosenNode[0]).not.toBeNull();
|
||||||
expect(component.isSelectionValid).not.toBeNull();
|
expect(component.isSelectionValid).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1070,21 +1063,21 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
|
|
||||||
it('should NOT be null after clicking on a node in the list (onNodeSelect)', async(() => {
|
it('should NOT be null after clicking on a node in the list (onNodeSelect)', async(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).not.toBeNull();
|
expect(nodes).not.toBeNull();
|
||||||
expect(component.chosenNode).not.toBeNull();
|
expect(component.chosenNode[0]).not.toBeNull();
|
||||||
|
expect(component.chosenNode[0].id).toBe('fakeid');
|
||||||
expect(component.isSelectionValid).not.toBeNull();
|
expect(component.isSelectionValid).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
component.onNodeSelect({ detail: { node: { entry } } });
|
component.onCurrentSelection([{ entry }]);
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should be null when the chosenNode is reset', async(() => {
|
it('should be null when the chosenNode is reset', async(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.onNodeSelect({ detail: { node: { entry: <Node> {} } } });
|
component.onCurrentSelection([{ entry: <Node> {} }]);
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
@ -1111,7 +1104,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
expect(nodes).not.toBeNull();
|
expect(nodes).not.toBeNull();
|
||||||
expect(component.chosenNode).not.toBeNull();
|
expect(component.chosenNode[0]).not.toBeNull();
|
||||||
expect(component.isSelectionValid).not.toBeNull();
|
expect(component.isSelectionValid).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1119,24 +1112,9 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should NOT be null after clicking on a node in the list (onNodeSelect)', async(() => {
|
|
||||||
component.select.subscribe((nodes) => {
|
|
||||||
expect(nodes).toBeDefined();
|
|
||||||
expect(nodes).not.toBeNull();
|
|
||||||
expect(component.chosenNode).not.toBeNull();
|
|
||||||
expect(component.isSelectionValid).not.toBeNull();
|
|
||||||
});
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.onNodeSelect({ detail: { node: { entry } } });
|
|
||||||
fixture.detectChanges();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should be null when the chosenNode is reset', async(() => {
|
it('should be null when the chosenNode is reset', async(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
component.onCurrentSelection([{ entry: <Node> {} }]);
|
||||||
component.onNodeSelect({ detail: { node: { entry: <Node> {} } } });
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.select.subscribe((nodes) => {
|
component.select.subscribe((nodes) => {
|
||||||
expect(nodes).toBeDefined();
|
expect(nodes).toBeDefined();
|
||||||
@ -1149,6 +1127,7 @@ describe('ContentNodeSelectorComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
SitesService
|
SitesService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { Node, NodePaging, Pagination, SiteEntry, SitePaging } from '@alfresco/js-api';
|
import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry } from '@alfresco/js-api';
|
||||||
import { DocumentListComponent } from '../document-list/components/document-list.component';
|
import { DocumentListComponent } from '../document-list/components/document-list.component';
|
||||||
import { RowFilter } from '../document-list/data/row-filter.model';
|
import { RowFilter } from '../document-list/data/row-filter.model';
|
||||||
import { ImageResolver } from '../document-list/data/image-resolver.model';
|
import { ImageResolver } from '../document-list/data/image-resolver.model';
|
||||||
@ -132,6 +132,10 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
pageSize: number = this.DEFAULT_PAGINATION.maxItems;
|
pageSize: number = this.DEFAULT_PAGINATION.maxItems;
|
||||||
|
|
||||||
|
/** Define the selection mode for document list. The allowed values are single or multiple */
|
||||||
|
@Input()
|
||||||
|
selectionMode: 'single' | 'multiple' = 'single';
|
||||||
|
|
||||||
/** Function used to decide if the selected node has permission to be selected.
|
/** Function used to decide if the selected node has permission to be selected.
|
||||||
* Default value is a function that always returns true.
|
* Default value is a function that always returns true.
|
||||||
*/
|
*/
|
||||||
@ -198,7 +202,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
showingSearchResults: boolean = false;
|
showingSearchResults: boolean = false;
|
||||||
loadingSearchResults: boolean = false;
|
loadingSearchResults: boolean = false;
|
||||||
inDialog: boolean = false;
|
inDialog: boolean = false;
|
||||||
_chosenNode: Node = null;
|
_chosenNode: Node [] = null;
|
||||||
folderIdToShow: string | null = null;
|
folderIdToShow: string | null = null;
|
||||||
breadcrumbFolderTitle: string | null = null;
|
breadcrumbFolderTitle: string | null = null;
|
||||||
startSiteGuid: string | null = null;
|
startSiteGuid: string | null = null;
|
||||||
@ -223,13 +227,9 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
private sitesService: SitesService) {
|
private sitesService: SitesService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
set chosenNode(value: Node) {
|
set chosenNode(value: Node[]) {
|
||||||
this._chosenNode = value;
|
this._chosenNode = value;
|
||||||
let valuesArray = null;
|
this.select.next(value);
|
||||||
if (value) {
|
|
||||||
valuesArray = [value];
|
|
||||||
}
|
|
||||||
this.select.next(valuesArray);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get chosenNode() {
|
get chosenNode() {
|
||||||
@ -334,7 +334,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
let folderNode: Node;
|
let folderNode: Node;
|
||||||
|
|
||||||
if (this.showingSearchResults && this.chosenNode) {
|
if (this.showingSearchResults && this.chosenNode) {
|
||||||
folderNode = this.chosenNode;
|
folderNode = this.chosenNode[0];
|
||||||
} else {
|
} else {
|
||||||
folderNode = this.documentList.folderNode;
|
folderNode = this.documentList.folderNode;
|
||||||
}
|
}
|
||||||
@ -468,7 +468,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private attemptNodeSelection(entry: Node): void {
|
private attemptNodeSelection(entry: Node): void {
|
||||||
if (entry && this.isSelectionValid(entry)) {
|
if (entry && this.isSelectionValid(entry)) {
|
||||||
this.chosenNode = entry;
|
this.chosenNode = [entry];
|
||||||
} else {
|
} else {
|
||||||
this.resetChosenNode();
|
this.resetChosenNode();
|
||||||
}
|
}
|
||||||
@ -482,12 +482,14 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when user selects a node
|
* It filters and emit the selection coming from the document list
|
||||||
*
|
*
|
||||||
* @param event CustomEvent for node-select
|
* @param nodesEntries
|
||||||
*/
|
*/
|
||||||
onNodeSelect(event: any): void {
|
onCurrentSelection(nodesEntries: NodeEntry[]): void {
|
||||||
this.attemptNodeSelection(event.detail.node.entry);
|
const validNodesEntity = nodesEntries.filter((node) => this.isSelectionValid(node.entry));
|
||||||
|
const nodes: Node[] = validNodesEntity.map((node) => node.entry );
|
||||||
|
this.chosenNode = nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitleIfCustomSite(site: SiteEntry) {
|
setTitleIfCustomSite(site: SiteEntry) {
|
||||||
|
@ -27,6 +27,7 @@ export interface ContentNodeSelectorComponentData {
|
|||||||
rowFilter?: any;
|
rowFilter?: any;
|
||||||
where?: string;
|
where?: string;
|
||||||
imageResolver?: any;
|
imageResolver?: any;
|
||||||
|
selectionMode: string;
|
||||||
isSelectionValid?: (entry: Node) => boolean;
|
isSelectionValid?: (entry: Node) => boolean;
|
||||||
breadcrumbTransform?: (node) => any;
|
breadcrumbTransform?: (node) => any;
|
||||||
excludeSiteContent?: string[];
|
excludeSiteContent?: string[];
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
[isSelectionValid]="data?.isSelectionValid"
|
[isSelectionValid]="data?.isSelectionValid"
|
||||||
[breadcrumbTransform]="data?.breadcrumbTransform"
|
[breadcrumbTransform]="data?.breadcrumbTransform"
|
||||||
[excludeSiteContent]="data?.excludeSiteContent"
|
[excludeSiteContent]="data?.excludeSiteContent"
|
||||||
|
[selectionMode]="data?.selectionMode"
|
||||||
[where]="data?.where"
|
[where]="data?.where"
|
||||||
[showSearch]="data?.showSearch"
|
[showSearch]="data?.showSearch"
|
||||||
[showDropdownSiteList]="data?.showDropdownSiteList"
|
[showDropdownSiteList]="data?.showDropdownSiteList"
|
||||||
@ -31,7 +32,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button mat-button
|
<button mat-button
|
||||||
[disabled]="!chosenNode"
|
[disabled]="!hasNodeSelected()"
|
||||||
class="adf-choose-action"
|
class="adf-choose-action"
|
||||||
(click)="onClick()"
|
(click)="onClick()"
|
||||||
data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }}
|
data-automation-id="content-node-selector-actions-choose">{{ buttonActionName | translate }}
|
||||||
|
@ -49,6 +49,10 @@ export class ContentNodeSelectorComponent {
|
|||||||
this.chosenNode = nodeList;
|
this.chosenNode = nodeList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasNodeSelected(): boolean {
|
||||||
|
return this.chosenNode?.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
onSiteChange(siteTitle: string) {
|
onSiteChange(siteTitle: string) {
|
||||||
this.updateTitle(siteTitle);
|
this.updateTitle(siteTitle);
|
||||||
}
|
}
|
||||||
|
@ -304,6 +304,10 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
|||||||
@Output()
|
@Output()
|
||||||
error: EventEmitter<any> = new EventEmitter();
|
error: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
/** Emitted when the node selection change */
|
||||||
|
@Output()
|
||||||
|
nodeSelected: EventEmitter<NodeEntry[]> = new EventEmitter<NodeEntry[]>();
|
||||||
|
|
||||||
@ViewChild('dataTable', { static: true })
|
@ViewChild('dataTable', { static: true })
|
||||||
dataTable: DataTableComponent;
|
dataTable: DataTableComponent;
|
||||||
|
|
||||||
@ -761,6 +765,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
|||||||
},
|
},
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
|
this.nodeSelected.emit(this.selection);
|
||||||
this.elementRef.nativeElement.dispatchEvent(domEvent);
|
this.elementRef.nativeElement.dispatchEvent(domEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,6 +778,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
|||||||
},
|
},
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
|
this.nodeSelected.emit(this.selection);
|
||||||
this.elementRef.nativeElement.dispatchEvent(domEvent);
|
this.elementRef.nativeElement.dispatchEvent(domEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,17 +108,22 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
openSelectDialog() {
|
openSelectDialog() {
|
||||||
const filesSaved: Node[] = [];
|
const selectedMode = this.field.params.multiple ? 'multiple' : 'single';
|
||||||
|
|
||||||
this.contentNodeSelectorService
|
this.contentNodeSelectorService
|
||||||
.openUploadFileDialog(this.field.form.contentHost)
|
.openUploadFileDialog(this.field.form.contentHost, '-my-', selectedMode)
|
||||||
.subscribe((selections: Node[]) => {
|
.subscribe((selections: Node[]) => {
|
||||||
selections.forEach(node => (node['isExternal'] = true));
|
selections.forEach(node => (node['isExternal'] = true));
|
||||||
filesSaved.push(selections[0]);
|
const selectionWithoutDuplication = this.removeExistingSelection(selections);
|
||||||
this.fixIncompatibilityFromPreviousAndNewForm(filesSaved);
|
this.fixIncompatibilityFromPreviousAndNewForm(selectionWithoutDuplication);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeExistingSelection(selections: Node[]) {
|
||||||
|
const existingNode: Node[] = [...this.field.value || []];
|
||||||
|
return selections.filter(opt => !existingNode.some( (node) => node.id === opt.id));
|
||||||
|
}
|
||||||
|
|
||||||
isContentSourceSelected(): boolean {
|
isContentSourceSelected(): boolean {
|
||||||
return (
|
return (
|
||||||
this.field.params &&
|
this.field.params &&
|
||||||
|
@ -32,7 +32,7 @@ export class ContentCloudNodeSelectorService {
|
|||||||
private dialog: MatDialog) {
|
private dialog: MatDialog) {
|
||||||
}
|
}
|
||||||
|
|
||||||
openUploadFileDialog(contentHost: string): Observable<Node[]> {
|
openUploadFileDialog(contentHost: string, currentFolderId?: string, selectionMode?: string): Observable<Node[]> {
|
||||||
const changedConfig = this.apiService.lastConfig;
|
const changedConfig = this.apiService.lastConfig;
|
||||||
changedConfig.provider = 'ALL';
|
changedConfig.provider = 'ALL';
|
||||||
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
|
changedConfig.hostEcm = contentHost.replace('/alfresco', '');
|
||||||
@ -44,8 +44,9 @@ export class ContentCloudNodeSelectorService {
|
|||||||
const data = <ContentNodeSelectorComponentData> {
|
const data = <ContentNodeSelectorComponentData> {
|
||||||
title: 'Select a file',
|
title: 'Select a file',
|
||||||
actionName: 'Choose',
|
actionName: 'Choose',
|
||||||
currentFolderId: '-my-',
|
currentFolderId,
|
||||||
select,
|
select,
|
||||||
|
selectionMode,
|
||||||
isSelectionValid: (entry: Node) => entry.isFile,
|
isSelectionValid: (entry: Node) => entry.isFile,
|
||||||
showFilesInResult: true
|
showFilesInResult: true
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
import { TRANSLATION_PROVIDER, CoreModule, FormRenderingService } from '@alfresco/adf-core';
|
import { TRANSLATION_PROVIDER, CoreModule, FormRenderingService } from '@alfresco/adf-core';
|
||||||
import { AppListCloudModule } from './app/app-list-cloud.module';
|
import { AppListCloudModule } from './app/app-list-cloud.module';
|
||||||
import { TaskCloudModule } from './task/task-cloud.module';
|
import { TaskCloudModule } from './task/task-cloud.module';
|
||||||
@ -54,9 +54,7 @@ import { ProcessServicesCloudPipeModule } from './pipes/process-services-cloud-p
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }
|
||||||
FormRenderingService,
|
|
||||||
{ provide: FormRenderingService, useClass: CloudFormRenderingService }
|
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
AppListCloudModule,
|
AppListCloudModule,
|
||||||
@ -69,4 +67,30 @@ import { ProcessServicesCloudPipeModule } from './pipes/process-services-cloud-p
|
|||||||
ProcessServicesCloudPipeModule
|
ProcessServicesCloudPipeModule
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ProcessServicesCloudModule { }
|
export class ProcessServicesCloudModule {
|
||||||
|
static forRoot(): ModuleWithProviders<ProcessServicesCloudModule> {
|
||||||
|
return {
|
||||||
|
ngModule: ProcessServicesCloudModule,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: TRANSLATION_PROVIDER,
|
||||||
|
multi: true,
|
||||||
|
useValue: {
|
||||||
|
name: 'adf-process-services-cloud',
|
||||||
|
source: 'assets/adf-process-services-cloud'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||||
|
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||||
|
FormRenderingService,
|
||||||
|
{ provide: FormRenderingService, useClass: CloudFormRenderingService }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static forChild(): ModuleWithProviders<ProcessServicesCloudModule> {
|
||||||
|
return {
|
||||||
|
ngModule: ProcessServicesCloudModule
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,7 +31,6 @@ import { ProcessDefinitionCloud } from '../models/process-definition-cloud.model
|
|||||||
import { Subject, Observable } from 'rxjs';
|
import { Subject, Observable } from 'rxjs';
|
||||||
import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model';
|
import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model';
|
||||||
import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe';
|
import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-cloud-start-process',
|
selector: 'adf-cloud-start-process',
|
||||||
templateUrl: './start-process-cloud.component.html',
|
templateUrl: './start-process-cloud.component.html',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user