Skip to content

Commit

Permalink
[AAE-4427] Embed upload progress dialog inside the upload from your d… (
Browse files Browse the repository at this point in the history
#6575)

* [AAE-4427] Embed upload progress dialog inside the upload from your device tab in attach file widget

* Fix failing unit tesT

* Add unit tests

* Removed not needed condition

* Make upload from your device tab same size as Repository tab

* Revert renaming causing breaking change

* simplify if conditions

* Update js-api version

* Use typescript ?. operator

* Add unit test for non existing datatable entries
  • Loading branch information
arditdomi committed Feb 4, 2021
1 parent a133678 commit d362153
Show file tree
Hide file tree
Showing 17 changed files with 211 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Shows a dialog listing all the files uploaded with the Upload Button or Drag Are
| Name | Type | Default value | Description |
| --- | --- | --- | --- |
| position | `string` | "right" | Dialog position. Can be 'left' or 'right'. |
| alwaysVisible | `boolean` | false | Dialog visibility. When true it makes the dialog visible even when there are no uploads. |

### Events

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
adf-highlight-selector=".adf-name-location-cell-name"
[showHeader]="false"
[node]="nodePaging"
[preselectNodes]="preselectNodes"
[preselectNodes]="preselectedNodes"
[maxItems]="pageSize"
[rowFilter]="_rowFilter"
[imageResolver]="imageResolver"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
*/

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { tick, ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { NodeEntry, Node, SiteEntry, SitePaging, NodePaging, ResultSetPaging, RequestScope } from '@alfresco/js-api';
import { SitesService, setupTestBed, NodesApiService } from '@alfresco/adf-core';
import { Node, NodeEntry, NodePaging, RequestScope, ResultSetPaging, SiteEntry, SitePaging } from '@alfresco/js-api';
import { FileModel, FileUploadStatus, NodesApiService, setupTestBed, SitesService, UploadService } from '@alfresco/adf-core';
import { of, throwError } from 'rxjs';
import { DropdownBreadcrumbComponent } from '../breadcrumb';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
Expand Down Expand Up @@ -65,6 +65,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
const nodeEntryEvent = new NodeEntryEvent(fakeNodeEntry);
let searchQueryBuilderService: SearchQueryBuilderService;
let contentNodeSelectorPanelService: ContentNodeSelectorPanelService;
let uploadService: UploadService;

function typeToSearchBox(searchTerm = 'string-to-search') {
const searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
Expand Down Expand Up @@ -95,6 +96,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
nodeService = TestBed.inject(NodesApiService);
sitesService = TestBed.inject(SitesService);
contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService);
uploadService = TestBed.inject(UploadService);
searchQueryBuilderService = component.queryBuilderService;
component.queryBuilderService.resetToDefaults();

Expand Down Expand Up @@ -1171,6 +1173,31 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
});

describe('interaction with upload functionality', () => {
let documentListService: DocumentListService;

beforeEach(() => {
documentListService = TestBed.inject(DocumentListService);

spyOn(documentListService, 'getFolderNode');
spyOn(documentListService, 'getFolder');
});

it('should remove the node from the chosenNodes when an upload gets deleted', () => {
fixture.detectChanges();
const selectSpy = spyOn(component.select, 'next');
const fakeFileModel = new FileModel(<File> { name: 'fake-name', size: 10000000 });
const fakeNodes = [<Node> { id: 'fakeNodeId' }, <Node> { id: 'fakeNodeId2' }];
fakeFileModel.data = { entry: fakeNodes[0] };
fakeFileModel.status = FileUploadStatus.Deleted;
component._chosenNode = [...fakeNodes];
uploadService.cancelUpload(fakeFileModel);

expect(selectSpy).toHaveBeenCalledWith([fakeNodes[1]]);
expect(component._chosenNode).toEqual([fakeNodes[1]]);
});
});

});

describe('Search panel', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ import {
NodesApiService,
SitesService,
UploadService,
FileUploadCompleteEvent
FileUploadCompleteEvent,
FileUploadDeleteEvent,
FileModel
} from '@alfresco/adf-core';
import { FormControl } from '@angular/forms';
import { Node, NodePaging, Pagination, SiteEntry, SitePaging, NodeEntry, QueryBody, RequestScope } from '@alfresco/js-api';
Expand Down Expand Up @@ -247,7 +249,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
searchInput: FormControl = new FormControl();

target: PaginatedComponent;
preselectNodes: NodeEntry[] = [];
preselectedNodes: NodeEntry[] = [];

searchPanelExpanded: boolean = false;

Expand Down Expand Up @@ -320,6 +322,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.breadcrumbTransform = this.breadcrumbTransform ? this.breadcrumbTransform : null;
this.isSelectionValid = this.isSelectionValid ? this.isSelectionValid : defaultValidation;
this.onFileUploadEvent();
this.onFileUploadDeletedEvent();
this.resetPagination();
this.setSearchScopeToNodes();

Expand Down Expand Up @@ -351,11 +354,30 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
takeUntil(this.onDestroy$)
)
.subscribe((uploadedFiles: FileUploadCompleteEvent[]) => {
this.preselectNodes = this.getPreselectNodesBasedOnSelectionMode(uploadedFiles);
this.preselectedNodes = this.getPreselectNodesBasedOnSelectionMode(uploadedFiles);
this.documentList.reload();
});
}

private onFileUploadDeletedEvent() {
this.uploadService.fileUploadDeleted
.pipe(takeUntil(this.onDestroy$))
.subscribe((deletedFileEvent: FileUploadDeleteEvent) => {
this.removeFromChosenNodes(deletedFileEvent.file);
this.documentList.reload();
});
}

private removeFromChosenNodes(file: FileModel) {
if (this.chosenNode) {
const fileIndex = this.chosenNode.findIndex((chosenNode: Node) => chosenNode.id === file.data.entry.id);
if (fileIndex !== -1) {
this._chosenNode.splice(fileIndex, 1);
this.select.next(this._chosenNode);
}
}
}

private getStartSite() {
this.nodesApiService.getNode(this.currentFolderId).subscribe((startNodeEntry) => {
this.startSiteGuid = this.sitesService.getSiteNameFromNodePath(startNodeEntry);
Expand Down Expand Up @@ -511,7 +533,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
this.showingSearchResults = false;
this.infiniteScroll = false;
this.breadcrumbFolderTitle = null;
this.preselectNodes = [];
this.preselectedNodes = [];
this.clearSearch();
this.navigationChange.emit($event);
}
Expand Down Expand Up @@ -573,7 +595,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
*/
onCurrentSelection(nodesEntries: NodeEntry[]): void {
const validNodesEntity = nodesEntries.filter((node) => this.isSelectionValid(node.entry));
this.chosenNode = validNodesEntity.map((node) => node.entry );
this.chosenNode = validNodesEntity.map((node) => node.entry);
}

setTitleIfCustomSite(site: SiteEntry) {
Expand All @@ -589,7 +611,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
}

hasPreselectNodes(): boolean {
return this.preselectNodes && this.preselectNodes.length > 0;
return this.preselectedNodes?.length > 0;
}

isSingleSelectionMode(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ <h2>{{title}}</h2>
mat-align-tabs="start"
(selectedIndexChange)="onTabSelectionChange($event)"
[class.adf-content-node-selector-headless-tabs]="!canPerformLocalUpload()">
<mat-tab label="{{ 'NODE_SELECTOR.FILE_SERVER' | translate }}">
<mat-tab label="{{ 'NODE_SELECTOR.REPOSITORY' | translate }}">
<adf-content-node-selector-panel
[currentFolderId]="data?.currentFolderId"
[restrictRootToCurrentFolderId]="data?.restrictRootToCurrentFolderId"
Expand All @@ -35,6 +35,11 @@ <h2>{{title}}</h2>
<mat-tab *ngIf="canPerformLocalUpload()"
label="{{ 'NODE_SELECTOR.UPLOAD_FROM_DEVICE' | translate }}"
[disabled]="isNotAllowedToUpload()">
<adf-upload-drag-area [rootFolderId]="currentDirectoryId">
<div class="adf-upload-dialog-container">
<adf-file-uploading-dialog [alwaysVisible]="true"></adf-file-uploading-dialog>
</div>
</adf-upload-drag-area>
</mat-tab>
</mat-tab-group>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
display: none;
}
}

.adf-upload-dialog {

&__content {
max-height: 64%;
}

height: 100%;
width: 100%;
position: unset;
bottom: unset;
}

.adf-upload-dialog-container {
height: 456px;
}
}

.adf-content-node-selector-dialog {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1532,7 +1532,7 @@ describe('DocumentList', () => {
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue(`assets/images/ft_ic_created.svg`);
});

it('should able to emit nodeSelected event with preselectNodes on the reload', async () => {
it('should able to emit nodeSelected event with preselectedNodes on the reload', async () => {
const nodeSelectedSpy = spyOn(documentList.nodeSelected, 'emit');

fixture.detectChanges();
Expand All @@ -1548,7 +1548,7 @@ describe('DocumentList', () => {
expect(nodeSelectedSpy).toHaveBeenCalled();
});

it('should be able to select first node from the preselectNodes when selectionMode set to single', async () => {
it('should be able to select first node from the preselectedNodes when selectionMode set to single', async () => {
documentList.selectionMode = 'single';
fixture.detectChanges();

Expand All @@ -1560,10 +1560,10 @@ describe('DocumentList', () => {
await fixture.whenStable();

expect(documentList.preselectNodes.length).toBe(2);
expect(documentList.getPreselectNodesBasedOnSelectionMode().length).toBe(1);
expect(documentList.getPreselectedNodesBasedOnSelectionMode().length).toBe(1);
});

it('should be able to select all preselectNodes when selectionMode set to multiple', async () => {
it('should be able to select all preselectedNodes when selectionMode set to multiple', async () => {
documentList.selectionMode = 'multiple';
fixture.detectChanges();

Expand All @@ -1575,13 +1575,14 @@ describe('DocumentList', () => {
await fixture.whenStable();

expect(documentList.preselectNodes.length).toBe(2);
expect(documentList.getPreselectNodesBasedOnSelectionMode().length).toBe(2);
expect(documentList.getPreselectedNodesBasedOnSelectionMode().length).toBe(2);
});

it('should call the datatable select row method for each preselected node', async () => {
const datatableSelectRowSpy = spyOn(documentList.dataTable, 'selectRow');
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
spyOn(documentList.data, 'getPreselectRows').and.returnValue(fakeDatatableRows);
spyOn(documentList.data, 'hasPreselectedRows').and.returnValue(true);
spyOn(documentList.data, 'getPreselectedRows').and.returnValue(fakeDatatableRows);

documentList.selectionMode = 'multiple';
documentList.preselectNodes = mockPreselectedNodes;
Expand All @@ -1593,7 +1594,7 @@ describe('DocumentList', () => {
expect(datatableSelectRowSpy.calls.count()).toEqual(fakeDatatableRows.length);
});

it('should not emit nodeSelected event when preselectNodes is undefined/empty', async () => {
it('should not emit nodeSelected event when preselectedNodes is undefined/empty', async () => {
const nodeSelectedSpy = spyOn(documentList.nodeSelected, 'emit');

fixture.detectChanges();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
if (this.data) {
if (changes.node && changes.node.currentValue) {
const merge = this._pagination ? this._pagination.merge : false;
this.data.loadPage(changes.node.currentValue, merge, null, this.getPreselectNodesBasedOnSelectionMode());
this.data.loadPage(changes.node.currentValue, merge, null, this.getPreselectedNodesBasedOnSelectionMode());
this.onPreselectNodes();
this.onDataReady(changes.node.currentValue);
} else if (changes.imageResolver) {
Expand All @@ -501,7 +501,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
this.resetSelection();
if (this.node) {
if (this.data) {
this.data.loadPage(this.node, this._pagination.merge, null, this.getPreselectNodesBasedOnSelectionMode());
this.data.loadPage(this.node, this._pagination.merge, null, this.getPreselectedNodesBasedOnSelectionMode());
}
this.onPreselectNodes();
this.syncPagination();
Expand Down Expand Up @@ -694,7 +694,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
onPageLoaded(nodePaging: NodePaging) {
if (nodePaging) {
if (this.data) {
this.data.loadPage(nodePaging, this._pagination.merge, this.allowDropFiles, this.getPreselectNodesBasedOnSelectionMode());
this.data.loadPage(nodePaging, this._pagination.merge, this.allowDropFiles, this.getPreselectedNodesBasedOnSelectionMode());
}
this.onPreselectNodes();
this.setLoadingState(false);
Expand Down Expand Up @@ -800,7 +800,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
}

onNodeSelect(event: { row: ShareDataRow, selection: Array<ShareDataRow> }) {
this.selection = event.selection.map((entry) => entry.node);
this.selection = event.selection.filter(entry => entry.node).map((entry) => entry.node);
const domEvent = new CustomEvent('node-select', {
detail: {
node: event.row ? event.row.node : null,
Expand Down Expand Up @@ -919,13 +919,13 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
this.error.emit(err);
}

getPreselectNodesBasedOnSelectionMode(): NodeEntry[] {
return this.hasPreselectNodes() ? (this.isSingleSelectionMode() ? [this.preselectNodes[0]] : this.preselectNodes) : [];
getPreselectedNodesBasedOnSelectionMode(): NodeEntry[] {
return this.hasPreselectedNodes() ? (this.isSingleSelectionMode() ? [this.preselectNodes[0]] : this.preselectNodes) : [];
}

onPreselectNodes() {
if (this.hasPreselectNodes()) {
const preselectedNodes = [...this.isSingleSelectionMode() ? [this.data.getPreselectRows()[0]] : this.data.getPreselectRows()];
if (this.data.hasPreselectedRows()) {
const preselectedNodes = [...this.isSingleSelectionMode() ? [this.data.getPreselectedRows()[0]] : this.data.getPreselectedRows()];
const selectedNodes = [...this.selection, ...preselectedNodes];

for (const node of preselectedNodes) {
Expand All @@ -939,7 +939,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
return this.selectionMode === 'single';
}

hasPreselectNodes(): boolean {
return this.preselectNodes && this.preselectNodes.length > 0;
hasPreselectedNodes(): boolean {
return this.preselectNodes?.length > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { DataColumn, DataRow, DataSorting, ContentService, ThumbnailService, setupTestBed } from '@alfresco/adf-core';
import { FileNode, FolderNode, SmartFolderNode, RuleFolderNode, LinkFolderNode, mockPreselectedNodes, mockNodePagingWithPreselectedNodes, mockNode2, fakeNodePaging } from './../../mock';
import { FileNode, FolderNode, SmartFolderNode, RuleFolderNode, LinkFolderNode, mockPreselectedNodes, mockNodePagingWithPreselectedNodes, mockNode2, fakeNodePaging, mockNode1 } from './../../mock';
import { ShareDataRow } from './share-data-row.model';
import { ShareDataTableAdapter } from './share-datatable-adapter';
import { ContentTestingModule } from '../../testing/content.testing.module';
Expand Down Expand Up @@ -484,28 +484,38 @@ describe('ShareDataTableAdapter', () => {

describe('Preselect rows', () => {

it('should set isSelected to be true for each preselectRow if the preselectNodes are defined', () => {
it('should set isSelected to be true for each preselectRow if the preselectedNodes are defined', () => {
const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []);
adapter.loadPage(mockNodePagingWithPreselectedNodes, null, null, mockPreselectedNodes);

expect(adapter.getPreselectRows().length).toBe(1);
expect(adapter.getPreselectRows()[0].isSelected).toBe(true);
expect(adapter.getPreselectedRows().length).toBe(1);
expect(adapter.getPreselectedRows()[0].isSelected).toBe(true);
});

it('should set preselectRows empty if preselectedNodes are undefined/empty', () => {
it('should set preselectedRows empty if preselectedNodes are undefined/empty', () => {
const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []);
adapter.loadPage(mockNodePagingWithPreselectedNodes, null, null, []);

expect(adapter.getPreselectRows().length).toBe(0);
expect(adapter.getPreselectedRows().length).toBe(0);
});

it('should set preselectRows empty if preselectedNodes are not found in the list', () => {
it('should set preselectedRows empty if preselectedNodes are not found in the list', () => {
const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []);
mockNode2.id = 'mock-file-id';
const preselectedNode = [ { entry: mockNode2 }];
adapter.loadPage(fakeNodePaging, null, null, preselectedNode);

expect(adapter.getPreselectRows().length).toBe(0);
expect(adapter.getPreselectedRows().length).toBe(0);
});

it('should preselected rows contain only the valid rows that exist in the datatable', () => {
const adapter = new ShareDataTableAdapter(thumbnailService, contentService, []);
const nonExistingEntry = {...mockNode1};
nonExistingEntry.id = 'non-existing-entry-id';
const preselectedNodes = [{ entry: nonExistingEntry }, { entry: mockNode1 }, { entry: mockNode2 }];
adapter.loadPage(mockNodePagingWithPreselectedNodes, null, null, preselectedNodes);

expect(adapter.getPreselectedRows().length).toBe(2);
});
});
});

0 comments on commit d362153

Please sign in to comment.