mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-4427] Embed upload progress dialog inside the upload from your d… (#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
This commit is contained in:
@@ -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
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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';
|
||||
@@ -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"]'));
|
||||
@@ -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();
|
||||
|
||||
@@ -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', () => {
|
||||
|
@@ -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';
|
||||
@@ -247,7 +249,7 @@ export class ContentNodeSelectorPanelComponent implements OnInit, OnDestroy {
|
||||
searchInput: FormControl = new FormControl();
|
||||
|
||||
target: PaginatedComponent;
|
||||
preselectNodes: NodeEntry[] = [];
|
||||
preselectedNodes: NodeEntry[] = [];
|
||||
|
||||
searchPanelExpanded: boolean = false;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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 {
|
||||
|
@@ -8,7 +8,7 @@
|
||||
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"
|
||||
@@ -35,6 +35,11 @@
|
||||
<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>
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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();
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
|
@@ -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) {
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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';
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -45,7 +45,7 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
permissionsStyle: PermissionStyleModel[];
|
||||
selectedRow: DataRow;
|
||||
allowDropFiles: boolean;
|
||||
preselectRows: DataRow[] = [];
|
||||
preselectedRows: DataRow[] = [];
|
||||
|
||||
set sortingMode(value: string) {
|
||||
let newValue = (value || 'client').toLowerCase();
|
||||
@@ -82,8 +82,8 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
this.sort();
|
||||
}
|
||||
|
||||
getPreselectRows(): Array<DataRow> {
|
||||
return this.preselectRows;
|
||||
getPreselectedRows(): Array<DataRow> {
|
||||
return this.preselectedRows;
|
||||
}
|
||||
|
||||
getColumns(): Array<DataColumn> {
|
||||
@@ -208,7 +208,7 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
}
|
||||
|
||||
private getNodeAspectNames(node: any): any[] {
|
||||
return node.entry && node.entry.aspectNames ? node.entry.aspectNames : node.aspectNames ? node.aspectNames : [];
|
||||
return node.entry?.aspectNames ? node.entry.aspectNames : (node.aspectNames ? node.aspectNames : []);
|
||||
}
|
||||
|
||||
private sortRows(rows: DataRow[], sorting: DataSorting) {
|
||||
@@ -218,7 +218,7 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
|
||||
const options: Intl.CollatorOptions = {};
|
||||
|
||||
if (sorting && sorting.key && rows && rows.length > 0) {
|
||||
if (sorting?.key && rows?.length) {
|
||||
|
||||
if (sorting.key.includes('sizeInBytes') || sorting.key === 'name') {
|
||||
options.numeric = true;
|
||||
@@ -255,9 +255,9 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
if (allowDropFiles !== undefined) {
|
||||
this.allowDropFiles = allowDropFiles;
|
||||
}
|
||||
if (nodePaging && nodePaging.list) {
|
||||
if (nodePaging?.list) {
|
||||
const nodeEntries: NodeEntry[] = nodePaging.list.entries;
|
||||
if (nodeEntries && nodeEntries.length > 0) {
|
||||
if (nodeEntries?.length) {
|
||||
shareDataRows = nodeEntries.map((item) => new ShareDataRow(item, this.contentService, this.permissionsStyle,
|
||||
this.thumbnailService, this.allowDropFiles));
|
||||
|
||||
@@ -267,7 +267,7 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
|
||||
if (this.sortingMode !== 'server') {
|
||||
// Sort by first sortable or just first column
|
||||
if (this.columns && this.columns.length > 0) {
|
||||
if (this.columns?.length) {
|
||||
const sorting = this.getSorting();
|
||||
if (sorting) {
|
||||
this.sortRows(shareDataRows, sorting);
|
||||
@@ -302,7 +302,7 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
}
|
||||
|
||||
selectRowsBasedOnGivenNodes(preselectNodes: NodeEntry[]) {
|
||||
if (preselectNodes && preselectNodes.length > 0) {
|
||||
if (preselectNodes?.length) {
|
||||
this.rows = this.rows.map((row) => {
|
||||
preselectNodes.map((preselectedNode) => {
|
||||
if (row.obj.entry.id === preselectedNode.entry.id) {
|
||||
@@ -313,7 +313,11 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
});
|
||||
}
|
||||
|
||||
this.preselectRows = [...this.rows.filter((res) => res.isSelected)];
|
||||
this.preselectedRows = [...this.rows.filter((res) => res.isSelected)];
|
||||
}
|
||||
|
||||
hasPreselectedRows(): boolean {
|
||||
return this.preselectedRows?.length > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -89,7 +89,7 @@
|
||||
"SELECT_LOCATION": "Select Location",
|
||||
"UPLOAD_BUTTON_SEARCH_WARNING_MESSAGE": "You can't upload a file whilst a search is still running",
|
||||
"UPLOAD_BUTTON_PERMISSION_WARNING_MESSAGE": "User doesn't have permission to upload content to the folder",
|
||||
"FILE_SERVER": "File server",
|
||||
"REPOSITORY": "Repository",
|
||||
"UPLOAD_FROM_DEVICE": "Upload from your device"
|
||||
},
|
||||
"OPERATION": {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div *ngIf="isDialogActive"
|
||||
<div *ngIf="canShowDialog()"
|
||||
role="dialog"
|
||||
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG'| translate"
|
||||
tabindex="0"
|
||||
@@ -89,14 +89,14 @@
|
||||
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.CANCEL_ALL' | translate"
|
||||
color="primary"
|
||||
mat-button
|
||||
*ngIf="!uploadList.isUploadCompleted() && !uploadList.isUploadCancelled()"
|
||||
*ngIf="canShowCancelAll()"
|
||||
(click)="toggleConfirmation()"
|
||||
>{{ 'ADF_FILE_UPLOAD.BUTTON.CANCEL_ALL' | translate }}</button>
|
||||
|
||||
<button
|
||||
id="adf-upload-dialog-close"
|
||||
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_CLOSE' | translate"
|
||||
*ngIf="uploadList.isUploadCompleted() || uploadList.isUploadCancelled()"
|
||||
*ngIf="canCloseDialog()"
|
||||
mat-button
|
||||
color="primary"
|
||||
(click)="close()"
|
||||
|
@@ -60,6 +60,50 @@ describe('FileUploadingDialogComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('Dialog and actions visibility', () => {
|
||||
it('should canShowDialog return true when alwaysVisible is true even when there are no uploads', () => {
|
||||
component.isDialogActive = false;
|
||||
component.alwaysVisible = true;
|
||||
|
||||
expect(component.canShowDialog()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not be able to close the dialog when alwaysVisible is set to true', () => {
|
||||
component.alwaysVisible = true;
|
||||
spyOn(component, 'hasUploadInProgress').and.returnValue(false);
|
||||
|
||||
expect(component.canCloseDialog()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not be able to close the dialog when has uploads are in progress', () => {
|
||||
component.alwaysVisible = false;
|
||||
spyOn(component, 'hasUploadInProgress').and.returnValue(true);
|
||||
|
||||
expect(component.canCloseDialog()).toBe(false);
|
||||
});
|
||||
|
||||
it('should be able to close the dialog when no uploads are in progress', () => {
|
||||
component.alwaysVisible = false;
|
||||
spyOn(component, 'hasUploadInProgress').and.returnValue(false);
|
||||
|
||||
expect(component.canCloseDialog()).toBe(true);
|
||||
});
|
||||
|
||||
it('should show cancel all when there are uploads in progress', () => {
|
||||
component.filesUploadingList = fileList;
|
||||
spyOn(component, 'hasUploadInProgress').and.returnValue(true);
|
||||
|
||||
expect(component.canShowCancelAll()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not show cancel all when there are no uploads in progress', () => {
|
||||
component.filesUploadingList = fileList;
|
||||
spyOn(component, 'hasUploadInProgress').and.returnValue(false);
|
||||
|
||||
expect(component.canShowCancelAll()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upload service subscribers', () => {
|
||||
it('should not render dialog when uploading list is empty', () => {
|
||||
uploadService.addToQueue();
|
||||
@@ -322,9 +366,9 @@ describe('FileUploadingDialogComponent', () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const confirmDecription = fixture.nativeElement.querySelector('#confirmationDescription');
|
||||
expect(confirmDecription).not.toBeNull();
|
||||
expect(confirmDecription).toBeDefined();
|
||||
const confirmDescription = fixture.nativeElement.querySelector('#confirmationDescription');
|
||||
expect(confirmDescription).not.toBeNull();
|
||||
expect(confirmDescription).toBeDefined();
|
||||
|
||||
const confirmTitle = fixture.nativeElement.querySelector('#confirmationTitle');
|
||||
expect(confirmTitle).toBeDefined();
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FileModel, FileUploadStatus, UploadService, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { FileModel, FileUploadStatus, UploadService, UserPreferencesService, FileUploadDeleteEvent, FileUploadCompleteEvent } from '@alfresco/adf-core';
|
||||
import { ChangeDetectorRef, Component, Input, Output, EventEmitter, OnDestroy, OnInit, ViewChild, HostBinding, ElementRef } from '@angular/core';
|
||||
import { Subscription, merge, Subject } from 'rxjs';
|
||||
import { FileUploadingListComponent } from './file-uploading-list.component';
|
||||
@@ -39,6 +39,10 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
position: string = 'right';
|
||||
|
||||
/** Makes the dialog always visible even when there are no uploads. */
|
||||
@Input()
|
||||
alwaysVisible: boolean = false;
|
||||
|
||||
/** Emitted when a file in the list has an error. */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter();
|
||||
@@ -107,7 +111,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
||||
this.uploadService.fileUploadDeleted
|
||||
)
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe(event => {
|
||||
.subscribe((event: FileUploadCompleteEvent | FileUploadDeleteEvent) => {
|
||||
this.totalCompleted = event.totalComplete;
|
||||
this.changeDetector.detectChanges();
|
||||
});
|
||||
@@ -201,4 +205,20 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
canShowDialog(): boolean {
|
||||
return this.isDialogActive || this.alwaysVisible;
|
||||
}
|
||||
|
||||
canShowCancelAll(): boolean {
|
||||
return this.filesUploadingList?.length && this.hasUploadInProgress();
|
||||
}
|
||||
|
||||
canCloseDialog(): boolean {
|
||||
return !this.hasUploadInProgress() && !this.alwaysVisible;
|
||||
}
|
||||
|
||||
hasUploadInProgress(): boolean {
|
||||
return (!this.uploadList?.isUploadCompleted() && !this.uploadList?.isUploadCancelled());
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ export class ContentNodeSelectorDialogPage {
|
||||
tabPage: TabPage = new TabPage();
|
||||
|
||||
uploadFromLocalTabName = 'Upload from your device';
|
||||
fileServerTabName = 'File server';
|
||||
repositoryTabName = 'Repository';
|
||||
|
||||
async checkDialogIsDisplayed(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.dialog);
|
||||
@@ -151,7 +151,7 @@ export class ContentNodeSelectorDialogPage {
|
||||
await BrowserVisibility.waitUntilElementIsPresent(uploadButton);
|
||||
await uploadButton.sendKeys(fileLocation);
|
||||
|
||||
await this.tabPage.clickTabByLabel(this.fileServerTabName);
|
||||
await this.tabPage.clickTabByLabel(this.repositoryTabName);
|
||||
|
||||
await this.dataTable.waitForTableBody();
|
||||
await this.dataTable.waitTillContentLoaded();
|
||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@@ -5,9 +5,9 @@
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@alfresco/js-api": {
|
||||
"version": "4.3.0-f117dba26d7c31cb56a6cf40eb84dff89a174971",
|
||||
"resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-4.3.0-f117dba26d7c31cb56a6cf40eb84dff89a174971.tgz",
|
||||
"integrity": "sha512-aNDqBYpfRVem1UivNWW0VAO+e8JZIo7ATTsf+MrIWWkM+2JiKKc+ByG8uj5HjGt0KekwlZh/tXbAwbNHvpo+PA==",
|
||||
"version": "4.3.0-2e84309af123ae96cc85bd9683cb1ab9a7119c33",
|
||||
"resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-4.3.0-2e84309af123ae96cc85bd9683cb1ab9a7119c33.tgz",
|
||||
"integrity": "sha512-D655f7pIzecQ+dN2hN8g/MrI0ToRuqO2EddOSs5g6d1fRwA4I32rpH62R2+5oXsdvgkLCuPJ8xoLamz3QVcajQ==",
|
||||
"requires": {
|
||||
"event-emitter": "^0.3.5",
|
||||
"minimatch": "3.0.4",
|
||||
|
@@ -71,7 +71,7 @@
|
||||
"process services-cloud"
|
||||
],
|
||||
"dependencies": {
|
||||
"@alfresco/js-api": "^4.3.0-f117dba26d7c31cb56a6cf40eb84dff89a174971",
|
||||
"@alfresco/js-api": "4.3.0-2e84309af123ae96cc85bd9683cb1ab9a7119c33",
|
||||
"@angular/animations": "^10.0.4",
|
||||
"@angular/cdk": "10.1.3",
|
||||
"@angular/common": "^10.0.4",
|
||||
|
Reference in New Issue
Block a user