Skip to content

Commit

Permalink
[AAE-6165] Add capability in the Attach File of displaying metadata f…
Browse files Browse the repository at this point in the history
…ields in addition to file name (#7324)

* [AAE-6165] Add capability in the Attach File of displaying metadata fields in addition to file name

* Minor css styling adjustments

* [AAE-6165] Changes done for date datatype and table css

* [AAE-6165] Changes done as per the comments

* [AAE-6165] Changes done as per comments on PR

* Resolved failing lints

* Updated CSS for attach file widget

* Updated css

* Updated UT

* Resolved e2e failures

* Resolved e2e errors

Co-authored-by: amohammedalfresco <abdul.mohammed@alfresco.com>
  • Loading branch information
Sushmitha-Vk and amohammedalfresco committed Oct 31, 2021
1 parent 1467b6d commit f14d333
Show file tree
Hide file tree
Showing 15 changed files with 452 additions and 73 deletions.
@@ -0,0 +1,26 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* tslint:disable:component-selector */

export interface DisplayableCMProperties {
name?: string;
prefixedName?: string;
title?: string;
dataType?: string;
defaultValue?: string;
}
2 changes: 2 additions & 0 deletions lib/core/form/components/widgets/core/form-field-metadata.ts
Expand Up @@ -17,6 +17,7 @@

/* tslint:disable:component-selector */

import { DisplayableCMProperties } from './displayable-cm-properties.model';
import { FormFieldFileSource } from './form-field-file-source';

export interface FormFieldMetadata {
Expand All @@ -35,4 +36,5 @@ export interface FormFieldMetadata {
retrieveMetadata?: boolean,
remove?: boolean
};
displayableCMProperties?: DisplayableCMProperties[];
}
1 change: 1 addition & 0 deletions lib/core/i18n/en.json
Expand Up @@ -42,6 +42,7 @@
"REMOVE_FILE": "Remove",
"UPLOAD": "UPLOAD",
"REQUIRED": "*Required",
"FILE_NAME": "File Name",
"NO_FILE_ATTACHED" : "No file attached",
"VALIDATOR": {
"INVALID_NUMBER": "Use a different number format",
Expand Down
5 changes: 4 additions & 1 deletion lib/process-services-cloud/karma.conf.js
Expand Up @@ -37,7 +37,10 @@ module.exports = function (config) {
'/assets/adf-core/i18n/en-GB.json': '/base/lib/core/i18n/en.json',
'/assets/adf-process-services-cloud/i18n/en.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
'/assets/adf-process-services-cloud/i18n/en-GB.json': '/base/lib/process-services-cloud/lib/i18n/en.json',
'/app.config.json': '/base/lib/config/app.config.json'
'/app.config.json': '/base/lib/config/app.config.json',
'/base/lib/process-services-cloud/assets/images/ft_ic_raster_image.svg': '/base/lib/process-services-cloud/assets/images/ft_ic_raster_image.svg',
'/base/lib/process-services-cloud/assets/images/ft_ic_miscellaneous.svg': '/base/lib/process-services-cloud/assets/images/ft_ic_miscellaneous.svg',
'/base/lib/process-services-cloud/assets/images/ft_ic_pdf.svg': '/base/lib/process-services-cloud/assets/images/ft_ic_pdf.svg',
},
plugins: [
require('karma-jasmine-ajax'),
Expand Down
@@ -1,64 +1,40 @@
<div class="adf-attach-widget {{field.className}}" [class.adf-invalid]="!field.isValid"
[class.adf-readonly]="field.readOnly">
<label class="adf-label" [attr.for]="field.id">{{field.name}}
<span *ngIf="isRequired()">*</span>
</label>
<div class="adf-attach-widget-container">
<div class="adf-attach-widget__menu-upload" *ngIf="isUploadButtonVisible()">
<button (click)="openSelectDialog()" mat-raised-button color="primary" [id]="field.id"
[matTooltip]="field.tooltip" matTooltipPosition="above" matTooltipShowDelay="1000">
{{ 'FORM.FIELD.ATTACH' | translate }}
<mat-icon>{{getWidgetIcon()}}</mat-icon>
</button>
<div class="adf-attach-file-widget-container">
<div class="adf-attach-widget {{field.className}}" [class.adf-invalid]="!field.isValid"
[class.adf-readonly]="field.readOnly">
<label class="adf-label" [attr.for]="field.id">{{field.name}}
<span *ngIf="isRequired()">*</span>
</label>
<div class="adf-attach-widget-container">
<div class="adf-attach-widget__menu-upload" *ngIf="isUploadButtonVisible()">
<button (click)="openSelectDialog()" mat-raised-button color="primary" [id]="field.id"
[matTooltip]="field.tooltip" matTooltipPosition="above" matTooltipShowDelay="1000">
{{ 'FORM.FIELD.ATTACH' | translate }}
<mat-icon>{{getWidgetIcon()}}</mat-icon>
</button>
</div>
</div>
</div>
</div>

<div id="adf-attach-widget-readonly-list">
<mat-list *ngIf="hasFile">
<mat-list-item
[ngClass]="{'adf-attach-files-row': true, 'adf-attach-selected-file-row': displayMenuOption('retrieveMetadata') && selectedNode && file.id === selectedNode.id}"
*ngFor="let file of uploadedFiles">
<mat-icon mat-list-icon class="adf-datatable-selected" *ngIf="selectedNode && file.id === selectedNode.id" (click)="onRowClicked(file)">
check_circle
</mat-icon>
<img mat-list-icon class="adf-attach-widget__icon" *ngIf="!selectedNode || file.id !== selectedNode.id" [id]="'file-'+file?.id+'-icon'" (click)="onRowClicked(file)"
[src]="file.content ? getIcon(file.content.mimeType) : getIcon(file['mimeType'])" [alt]="mimeTypeIcon"
role="button" tabindex="0" />
<span matLine id="{{'file-'+file?.id}}" role="button" tabindex="0" class="adf-file" (click)="onRowClicked(file)">{{file.name}}</span>
<button id="{{'file-'+file?.id+'-option-menu'}}" mat-icon-button [matMenuTriggerFor]="fileActionMenu" *ngIf="!!file.content?.mimeType">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #fileActionMenu="matMenu" xPosition="before">
<button *ngIf="displayMenuOption('show') && !!file.content?.mimeType" id="{{'file-'+file?.id+'-show-file'}}"
mat-menu-item (click)="onAttachFileClicked(file)">
<mat-icon>visibility</mat-icon>
<span>{{ 'FORM.FIELD.VIEW_FILE' | translate }}</span>
</button>
<button *ngIf="displayMenuOption('download') && !!file.content?.mimeType" id="{{'file-'+file?.id+'-download-file'}}"
mat-menu-item (click)="downloadContent(file)">
<mat-icon>file_download</mat-icon>
<span>{{ 'FORM.FIELD.DOWNLOAD_FILE' | translate }}</span>
</button>
<button *ngIf="displayMenuOption('retrieveMetadata') && !!file.content?.mimeType" id="{{'file-'+file?.id+'-retrieve-file-metadata'}}"
mat-menu-item (click)="contentModelFormFileHandler(file)">
<mat-icon class="mat-24">low_priority</mat-icon>
<span>{{ 'ADF_CLOUD_FORM_COMPONENT.RETRIEVE_METADATA' | translate }}</span>
</button>
<button *ngIf="!field.readOnly && displayMenuOption('remove')" id="{{'file-'+file?.id+'-remove-file'}}"
mat-menu-item [id]="'file-'+file?.id+'-remove'"
(click)="onRemoveAttachFile(file);" (keyup.enter)="onRemoveAttachFile(file);">
<mat-icon class="mat-24">highlight_off</mat-icon>
<span>{{ 'FORM.FIELD.REMOVE_FILE' | translate }}</span>
</button>
</mat-menu>
</mat-list-item>
</mat-list>
<div *ngIf="!hasFile && field.readOnly" id="{{'adf-attach-empty-list-'+field.id}}">
{{ 'FORM.FIELD.NO_FILE_ATTACHED' | translate }}
<div id="adf-attach-widget-readonly-list" class="adf-attach-widget-readonly-table">
<adf-cloud-file-properties-table
[uploadedFiles]="uploadedFiles"
[hasFile]="hasFile"
[selectedNode]="selectedNode"
[field]="field"
[displayedColumns]="displayedColumns"
[mimeTypeIcon]="mimeTypeIcon"
(rowClick)="onRowClicked($event)"
(attachFileClick)="onAttachFileClicked($event)"
(downloadFile)="downloadContent($event)"
(contentModelFileHandler)="contentModelFormFileHandler($event)"
(removeAttachFile)="onRemoveAttachFile($event)"
></adf-cloud-file-properties-table>
<div *ngIf="!hasFile && field.readOnly" id="{{'adf-attach-empty-list-'+field.id}}">
{{ 'FORM.FIELD.NO_FILE_ATTACHED' | translate }}
</div>

</div>

<error-widget [error]="field.validationSummary"></error-widget>
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
</div>

<error-widget [error]="field.validationSummary"></error-widget>
<error-widget *ngIf="isInvalidFieldRequired()" required="{{ 'FORM.FIELD.REQUIRED' | translate }}"></error-widget>
@@ -1,6 +1,5 @@
.adf {
&-attach-widget-container {
margin-bottom: 15px;
display: flex;
align-items: center;

Expand All @@ -19,6 +18,26 @@
&-attach-widget__menu-upload {
display: flex;
align-items: center;
margin-right: 8px;

.mat-raised-button {
line-height: 28px;
font-size: 12px;
}

button span {
font-size: 12px;

mat-icon {
line-height: 12px;
height: 12px;
width: 12px;
}

.material-icons {
font-size: 18px;
}
}
}

&-attach-widget__input-type {
Expand Down Expand Up @@ -47,10 +66,22 @@
}

&-attach-widget {
width: 100%;
display: flex;
justify-content: space-between;
word-break: break-all;
padding: 0.4375em 0;
border-top: 0.84375em solid transparent;
padding: 0.4375em;
border-bottom: none;
background: var(--theme-colors-mat-grey);
min-height: 27px;

.adf-label {
width: 32px;
font-size: var(--theme-caption-font-size);
line-height: var(--theme-headline-line-height);
text-align: left;
white-space: nowrap;
margin-left: 8px;
}
}

&-attach-widget__icon {
Expand Down Expand Up @@ -83,4 +114,8 @@
}
}
}

&-attach-file-widget-container {
margin: 15px;
}
}
Expand Up @@ -29,7 +29,8 @@ import {
FormService,
DownloadService,
AppConfigService,
UploadWidgetContentLinkModel
UploadWidgetContentLinkModel,
LocalizedDatePipe
} from '@alfresco/adf-core';
import {
allSourceParams,
Expand All @@ -54,7 +55,9 @@ import {
processVariables,
mockAllFileSourceWithRenamedFolderVariablePathType,
allSourceParamsWithRelativePath,
fakeLocalPhysicalRecordResponse
fakeLocalPhysicalRecordResponse,
displayableCMParams,
fakeLocalPngHavingCMProperties
} from '../../../mocks/attach-file-cloud-widget.mock';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
Expand All @@ -79,6 +82,7 @@ describe('AttachFileCloudWidgetComponent', () => {
let updateFormSpy: jasmine.Spy;
let contentClickedSpy: jasmine.Spy;
let openUploadFileDialogSpy: jasmine.Spy;
let localizedDataPipe: LocalizedDatePipe;

function createUploadWidgetField(form: FormModel, fieldId: string, value?: any, params?: any, multiple?: boolean, name?: string, readOnly?: boolean) {
widget.field = new FormFieldModel(form, {
Expand Down Expand Up @@ -122,6 +126,7 @@ describe('AttachFileCloudWidgetComponent', () => {
formService = TestBed.inject(FormService);
contentNodeSelectorPanelService = TestBed.inject(ContentNodeSelectorPanelService);
openUploadFileDialogSpy = spyOn(contentCloudNodeSelectorService, 'openUploadFileDialog').and.returnValue(of([fakeMinimalNode]));
localizedDataPipe = new LocalizedDatePipe();
});

afterEach(() => {
Expand Down Expand Up @@ -197,6 +202,45 @@ describe('AttachFileCloudWidgetComponent', () => {
expect(contentNodeSelectorPanelService.customModels).toEqual([]);
});

describe('Upload widget with displayable ContentModel properties', () => {

it('should display CM Properties if the file contains value', async() => {
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngHavingCMProperties], displayableCMParams);
fixture.detectChanges();
await fixture.whenStable();

expect(element.querySelector('#file-1155-icon')).not.toBeNull();
expect(element.querySelector('#fileProperty-1155-name').textContent).toBe('Alex');
expect(element.querySelector('#fileProperty-1155-age').textContent).toBe('34');
});

it('should display defaultValue if the file does not contain value for respective displayableCMProperties', async() => {
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngResponse], displayableCMParams);
fixture.detectChanges();
await fixture.whenStable();

expect(element.querySelector('#fileProperty-1155-name').textContent).toBe('Bob');
expect(element.querySelector('#fileProperty-1155-age').textContent).toBe('--');
});

it('should not display CM Properties in table if the field does not contain displayableCMProperties', async() => {
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngHavingCMProperties], allSourceParams);
fixture.detectChanges();
await fixture.whenStable();

expect(element.querySelector('#fileProperty-1155-name')).toBeNull();
expect(element.querySelector('#fileProperty-1155-age')).toBeNull();
});

it('should display date property in converted form based on dataType', async() => {
createUploadWidgetField(new FormModel(), 'attach-file-alfresco', [fakeLocalPngHavingCMProperties], displayableCMParams);
fixture.detectChanges();
await fixture.whenStable();

expect(element.querySelector('#fileProperty-1155-dob').textContent).toBe(localizedDataPipe.transform(new Date()));
});
});

describe('destinationFolderPath', () => {

it('should be able to fetch nodeId if destinationFolderPath is defined', async () => {
Expand Down
Expand Up @@ -74,6 +74,7 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
return this._nodesApi;
}
displayedColumns = ['icon', 'fileName', 'action'];

constructor(
formService: FormService,
Expand All @@ -95,6 +96,8 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
const files = this.field.value || this.field.form.values[this.field.id];
this.contentModelFormFileHandler(files[0]);
}
this.field.params.displayableCMProperties = this.field.params.displayableCMProperties ?? [];
this.displayedColumns.splice(2, 0, ...this.field.params.displayableCMProperties?.map(property => property?.name));
}

isPathStaticType(): boolean {
Expand Down Expand Up @@ -221,10 +224,6 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i
return this.isAlfrescoAndLocal() ? 'file_upload' : 'attach_file';
}

displayMenuOption(option: string): boolean {
return this.field?.params?.menuOptions ? this.field.params.menuOptions[option] : option !== AttachFileCloudWidgetComponent.RETRIEVE_METADATA_OPTION;
}

onRowClicked(file?: Node) {
if (this.selectedNode?.id === file?.id) {
this.selectedNode = null;
Expand Down

0 comments on commit f14d333

Please sign in to comment.