mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-1986] Content matadata editing phase II. (#2796)
* Aspects collection * Fetch only those metadata aspects which are defined in the application config * Aspect property filtering first round * Addig wildcard support for preset, default preset fallback to wildcard, and logging * Add white list service * Renaming services * ContentMetadataService and CardViewItemDispatcherComponent update * Observables... Observables everywhere... * Propers CardViewAspect * Defining more interfaces * Dummy data and expansions panels * Fix build attempt & proper panel expansion * Folder restructuring * Add different type mappings * Restructuring Card view component * Fix build * More ECM types supported * Validators first phase, extraction of interfaces, world domination preparation * Validators phase II. * Integer and float validators * Hide empty text items and validation message foundation * Validation error messages for text item view, small style changes * Update date item * bool item component * Datetimepicker npm module * Datetime model * Add mapping for datetime * Small fixes * Passing down preset * Adding forgotten package.json entry * Adding some tests for wrapper card component * content-metadata.component tests * Covering some edge cases, adding some tests * Fix cardview component's test * Add datetimepicker to demoshell * card view component show empty values by default * displayEmpty dependency injection * remove table like design from cardview * Using alfresco-js-api instead of spike * Remove spike folder and contents * Fix test * Cardview updated docs * Content metadata md * Fix review issues * Fix the packagr issue
This commit is contained in:
committed by
Eugenio Romano
parent
994041fb23
commit
783f7f0497
@@ -0,0 +1,28 @@
|
||||
<mat-card *ngIf="node">
|
||||
<mat-card-content>
|
||||
<adf-content-metadata
|
||||
[node]="node"
|
||||
[displayEmpty]="displayEmpty"
|
||||
[editable]="editable"
|
||||
[expanded]="expanded"
|
||||
[preset]="preset">
|
||||
</adf-content-metadata>
|
||||
</mat-card-content>
|
||||
<mat-card-footer class="adf-content-metadata-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
<div>
|
||||
<button mat-icon-button (click)="toggleEdit()" data-automation-id="mata-data-card-toggle-edit">
|
||||
<mat-icon>mode_edit</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-button (click)="toggleExpanded()" data-automation-id="mata-data-card-toggle-expand">
|
||||
<ng-container *ngIf="!expanded">
|
||||
<span data-automation-id="mata-data-card-toggle-expand-label">{{ 'ADF_VIEWER.SIDEBAR.METADATA.MORE_INFORMATION' | translate }}</span>
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="expanded">
|
||||
<span data-automation-id="mata-data-card-toggle-expand-label">{{ 'ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION' | translate }}</span>
|
||||
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||
</ng-container>
|
||||
</button>
|
||||
</mat-card-footer>
|
||||
</mat-card>
|
@@ -3,9 +3,18 @@
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf-viewer-default-sidebar {
|
||||
.adf-content-metadata-card {
|
||||
|
||||
&-card-footer.mat-card-footer {
|
||||
.mat-card {
|
||||
padding: 0;
|
||||
|
||||
.mat-card-content {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-footer.mat-card-footer {
|
||||
margin: 0;
|
||||
padding: 8px 12px;
|
||||
border-top: 1px solid mat-color($foreground, text, 0.07);
|
||||
|
@@ -0,0 +1,172 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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: ban*/
|
||||
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { ContentMetadataCardComponent } from './content-metadata-card.component';
|
||||
import { ContentMetadataComponent } from '../content-metadata/content-metadata.component';
|
||||
import { MatExpansionModule, MatCardModule, MatButtonModule, MatIconModule } from '@angular/material';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
||||
import { PropertyDescriptorLoaderService } from '../../services/properties-loader.service';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import { AspectWhiteListService } from '../../services/aspect-whitelist.service';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
|
||||
describe('ContentMetadataCardComponent', () => {
|
||||
|
||||
let component: ContentMetadataCardComponent,
|
||||
fixture: ComponentFixture<ContentMetadataCardComponent>,
|
||||
node: MinimalNodeEntryEntity,
|
||||
preset = 'custom-preset';
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MatExpansionModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatIconModule
|
||||
],
|
||||
declarations: [
|
||||
ContentMetadataCardComponent,
|
||||
ContentMetadataComponent
|
||||
],
|
||||
providers: [
|
||||
ContentMetadataService,
|
||||
BasicPropertiesService,
|
||||
PropertyDescriptorLoaderService,
|
||||
PropertyDescriptorsService,
|
||||
AspectWhiteListService,
|
||||
AlfrescoApiService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentMetadataCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
node = <MinimalNodeEntryEntity> {
|
||||
aspectNames: [],
|
||||
content: {},
|
||||
properties: {},
|
||||
createdByUser: {},
|
||||
modifiedByUser: {}
|
||||
};
|
||||
|
||||
component.node = node;
|
||||
component.preset = preset;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should have displayEmpty input param as false by default', () => {
|
||||
expect(component.displayEmpty).toBe(false);
|
||||
});
|
||||
|
||||
it('should pass through the node to the underlying component', () => {
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance;
|
||||
|
||||
expect(contentMetadataComponent.node).toBe(node);
|
||||
});
|
||||
|
||||
it('should pass through the preset to the underlying component', () => {
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance;
|
||||
|
||||
expect(contentMetadataComponent.preset).toBe(preset);
|
||||
});
|
||||
|
||||
it('should pass through the preset to the underlying component', () => {
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance;
|
||||
|
||||
expect(contentMetadataComponent.displayEmpty).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass through the editable to the underlying component', () => {
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance;
|
||||
|
||||
expect(contentMetadataComponent.editable).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass through the expanded to the underlying component', () => {
|
||||
component.expanded = true;
|
||||
fixture.detectChanges();
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent)).componentInstance;
|
||||
|
||||
expect(contentMetadataComponent.expanded).toBe(true);
|
||||
});
|
||||
|
||||
it('should not show anything if node is not present', () => {
|
||||
component.node = undefined;
|
||||
fixture.detectChanges();
|
||||
|
||||
const contentMetadataComponent = fixture.debugElement.query(By.directive(ContentMetadataComponent));
|
||||
|
||||
expect(contentMetadataComponent).toBeNull();
|
||||
});
|
||||
|
||||
it('should toggle editable by clicking on the button', () => {
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-edit"]'));
|
||||
button.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.editable).toBe(false);
|
||||
});
|
||||
|
||||
it('should toggle expanded by clicking on the button', () => {
|
||||
component.expanded = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-expand"]'));
|
||||
button.triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.expanded).toBe(false);
|
||||
});
|
||||
|
||||
it('should have the proper text on button while collapsed', () => {
|
||||
component.expanded = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonLabel = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-expand-label"]'));
|
||||
|
||||
expect(buttonLabel.nativeElement.innerText.trim()).toBe('ADF_VIEWER.SIDEBAR.METADATA.MORE_INFORMATION');
|
||||
});
|
||||
|
||||
it('should have the proper text on button while collapsed', () => {
|
||||
component.expanded = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const buttonLabel = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-expand-label"]'));
|
||||
|
||||
expect(buttonLabel.nativeElement.innerText.trim()).toBe('ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION');
|
||||
});
|
||||
});
|
@@ -18,19 +18,23 @@
|
||||
import { Component, Input, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
const PROPERTY_COUNTER_WHILE_COLLAPSED = 5;
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-metadata-card',
|
||||
templateUrl: './content-metadata-card.component.html',
|
||||
styleUrls: ['./content-metadata-card.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { 'class': 'adf-viewer-default-sidebar' }
|
||||
host: { 'class': 'adf-content-metadata-card' }
|
||||
})
|
||||
export class ContentMetadataCardComponent {
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
@Input()
|
||||
displayEmpty: boolean = false;
|
||||
|
||||
@Input()
|
||||
preset: string;
|
||||
|
||||
editable: boolean = false;
|
||||
expanded: boolean = false;
|
||||
|
||||
@@ -41,8 +45,4 @@ export class ContentMetadataCardComponent {
|
||||
toggleExpanded(): void {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
get maxPropertiesToShow(): number {
|
||||
return this.expanded ? Infinity : PROPERTY_COUNTER_WHILE_COLLAPSED;
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
<div class="adf-metadata-properties">
|
||||
<mat-accordion displayMode="flat">
|
||||
<mat-expansion-panel [expanded]="!expanded" [hideToggle]="!expanded">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<adf-card-view
|
||||
class="adf-metadata-properties-basic"
|
||||
data-automation-id="adf-metadata-properties-basic"
|
||||
[properties]="basicProperties$ | async"
|
||||
[editable]="editable"
|
||||
[displayEmpty]="displayEmpty">
|
||||
</adf-card-view>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<ng-container *ngIf="expanded">
|
||||
<ng-container *ngIf="aspects$ | async; else loading; let aspects">
|
||||
<div *ngFor="let aspect of aspects" class="adf-metadata-properties-aspect">
|
||||
<mat-expansion-panel>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
{{aspect.title}}
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<adf-card-view
|
||||
class="adf-node-aspect-properties"
|
||||
[properties]="aspect.properties"
|
||||
[editable]="editable"
|
||||
[displayEmpty]="displayEmpty">
|
||||
</adf-card-view>
|
||||
</mat-expansion-panel>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #loading>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</mat-accordion>
|
||||
</div>
|
@@ -0,0 +1,9 @@
|
||||
@mixin adf-content-metadata-theme($theme) {
|
||||
.adf {
|
||||
&-metadata-properties {
|
||||
.mat-expansion-panel:not([class*=mat-elevation-z]) {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,241 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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: ban*/
|
||||
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { ContentMetadataComponent } from './content-metadata.component';
|
||||
import { MatExpansionModule, MatButtonModule, MatIconModule } from '@angular/material';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
||||
import { PropertyDescriptorLoaderService } from '../../services/properties-loader.service';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import { AspectWhiteListService } from '../../services/aspect-whitelist.service';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { CardViewBaseItemModel, CardViewComponent, CardViewUpdateService, NodesApiService, LogService } from '@alfresco/adf-core';
|
||||
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
describe('ContentMetadataComponent', () => {
|
||||
|
||||
let component: ContentMetadataComponent,
|
||||
fixture: ComponentFixture<ContentMetadataComponent>,
|
||||
node: MinimalNodeEntryEntity,
|
||||
preset = 'custom-preset';
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MatExpansionModule,
|
||||
MatButtonModule,
|
||||
MatIconModule
|
||||
],
|
||||
declarations: [
|
||||
ContentMetadataComponent
|
||||
],
|
||||
providers: [
|
||||
ContentMetadataService,
|
||||
BasicPropertiesService,
|
||||
PropertyDescriptorLoaderService,
|
||||
PropertyDescriptorsService,
|
||||
AspectWhiteListService,
|
||||
AlfrescoApiService,
|
||||
NodesApiService,
|
||||
{ provide: LogService, useValue: { error: jasmine.createSpy('error') } }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentMetadataComponent);
|
||||
component = fixture.componentInstance;
|
||||
node = <MinimalNodeEntryEntity> {
|
||||
id: 'node-id',
|
||||
aspectNames: [],
|
||||
content: {},
|
||||
properties: {},
|
||||
createdByUser: {},
|
||||
modifiedByUser: {}
|
||||
};
|
||||
|
||||
component.node = node;
|
||||
component.preset = preset;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('Default input param values', () => {
|
||||
|
||||
it('should have editable input param as false by default', () => {
|
||||
expect(component.editable).toBe(false);
|
||||
});
|
||||
|
||||
it('should have displayEmpty input param as false by default', () => {
|
||||
expect(component.displayEmpty).toBe(false);
|
||||
});
|
||||
|
||||
it('should have expanded input param as false by default', () => {
|
||||
expect(component.expanded).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Saving', () => {
|
||||
|
||||
it('should save the node on itemUpdate', () => {
|
||||
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' },
|
||||
updateService: CardViewUpdateService = fixture.debugElement.injector.get(CardViewUpdateService),
|
||||
nodesApiService: NodesApiService = TestBed.get(NodesApiService);
|
||||
spyOn(nodesApiService, 'updateNode');
|
||||
|
||||
updateService.update(property, 'updated-value');
|
||||
|
||||
expect(nodesApiService.updateNode).toHaveBeenCalledWith('node-id', {
|
||||
'property-key': 'updated-value'
|
||||
});
|
||||
});
|
||||
|
||||
it('should update the node on successful save', async(() => {
|
||||
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' },
|
||||
updateService: CardViewUpdateService = fixture.debugElement.injector.get(CardViewUpdateService),
|
||||
nodesApiService: NodesApiService = TestBed.get(NodesApiService),
|
||||
expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
|
||||
|
||||
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
|
||||
return Observable.of(expectedNode);
|
||||
});
|
||||
|
||||
updateService.update(property, 'updated-value');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.node).toBe(expectedNode);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw error on unsuccessful save', () => {
|
||||
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' },
|
||||
updateService: CardViewUpdateService = fixture.debugElement.injector.get(CardViewUpdateService),
|
||||
nodesApiService: NodesApiService = TestBed.get(NodesApiService),
|
||||
logService: LogService = TestBed.get(LogService);
|
||||
|
||||
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
|
||||
return ErrorObservable.create(new Error('My bad'));
|
||||
});
|
||||
|
||||
updateService.update(property, 'updated-value');
|
||||
|
||||
expect(logService.error).toHaveBeenCalledWith(new Error('My bad'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Properties loading', () => {
|
||||
|
||||
let expectedNode,
|
||||
contentMetadataService: ContentMetadataService;
|
||||
|
||||
beforeEach(() => {
|
||||
expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
|
||||
contentMetadataService = TestBed.get(ContentMetadataService);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should load the basic properties on node change', () => {
|
||||
spyOn(contentMetadataService, 'getBasicProperties');
|
||||
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
|
||||
expect(contentMetadataService.getBasicProperties).toHaveBeenCalledWith(expectedNode);
|
||||
});
|
||||
|
||||
it('should pass through the loaded basic properties to the card view', async(() => {
|
||||
const expectedProperties = [];
|
||||
component.expanded = false;
|
||||
fixture.detectChanges();
|
||||
spyOn(contentMetadataService, 'getBasicProperties').and.callFake(() => {
|
||||
return Observable.of(expectedProperties);
|
||||
});
|
||||
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
|
||||
component.basicProperties$.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
const basicPropertiesComponent = fixture.debugElement.query(By.directive(CardViewComponent)).componentInstance;
|
||||
expect(basicPropertiesComponent.properties).toBe(expectedProperties);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should pass through the displayEmpty to the card view of basic properties', async(() => {
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
spyOn(contentMetadataService, 'getBasicProperties').and.returnValue(Observable.of([]));
|
||||
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
|
||||
component.basicProperties$.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
const basicPropertiesComponent = fixture.debugElement.query(By.directive(CardViewComponent)).componentInstance;
|
||||
expect(basicPropertiesComponent.displayEmpty).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should load the aspect properties on node change', () => {
|
||||
spyOn(contentMetadataService, 'getAspectProperties');
|
||||
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
|
||||
expect(contentMetadataService.getAspectProperties).toHaveBeenCalledWith(expectedNode, 'custom-preset');
|
||||
});
|
||||
|
||||
it('should pass through the loaded aspect properties to the card view', async(() => {
|
||||
const expectedProperties = [];
|
||||
component.expanded = true;
|
||||
fixture.detectChanges();
|
||||
spyOn(contentMetadataService, 'getAspectProperties').and.callFake(() => {
|
||||
return Observable.of([{ properties: expectedProperties }]);
|
||||
});
|
||||
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
|
||||
component.basicProperties$.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
const firstAspectPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-properties-aspect adf-card-view')).componentInstance;
|
||||
expect(firstAspectPropertiesComponent.properties).toBe(expectedProperties);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should pass through the displayEmpty to the card view of aspect properties', async(() => {
|
||||
component.expanded = true;
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
spyOn(contentMetadataService, 'getAspectProperties').and.returnValue(Observable.of([{ properties: [] }]));
|
||||
|
||||
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
|
||||
|
||||
component.basicProperties$.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
const basicPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-properties-aspect adf-card-view')).componentInstance;
|
||||
expect(basicPropertiesComponent.displayEmpty).toBe(false);
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
@@ -15,21 +15,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnInit, SimpleChanges, SimpleChange, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { CardViewItem, CardViewUpdateService, FileSizePipe, NodesApiService } from '@alfresco/adf-core';
|
||||
import { ContentMetadataService } from './content-metadata.service';
|
||||
import { CardViewItem, CardViewUpdateService, NodesApiService, LogService } from '@alfresco/adf-core';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { CardViewAspect } from '../../interfaces/content-metadata.interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-metadata',
|
||||
templateUrl: './content-metadata.component.html',
|
||||
styleUrls: ['./content-metadata.component.scss'],
|
||||
host: { 'class': 'adf-content-metadata' },
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
providers: [ CardViewUpdateService ],
|
||||
viewProviders: [ ContentMetadataService, FileSizePipe ]
|
||||
providers: [ CardViewUpdateService ]
|
||||
})
|
||||
export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
|
||||
@@ -40,43 +39,41 @@ export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
editable: boolean = false;
|
||||
|
||||
@Input()
|
||||
maxPropertiesToShow: number = Infinity;
|
||||
displayEmpty: boolean = false;
|
||||
|
||||
properties: CardViewItem[] = [];
|
||||
@Input()
|
||||
expanded: boolean = false;
|
||||
|
||||
@Input()
|
||||
preset: string;
|
||||
|
||||
basicProperties$: Observable<CardViewItem[]>;
|
||||
aspects$: Observable<CardViewAspect[]>;
|
||||
|
||||
constructor(private contentMetadataService: ContentMetadataService,
|
||||
private cardViewUpdateService: CardViewUpdateService,
|
||||
private nodesApi: NodesApiService) {}
|
||||
private nodesApi: NodesApiService,
|
||||
private logService: LogService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
ngOnInit() {
|
||||
this.cardViewUpdateService.itemUpdated$
|
||||
.switchMap(this.saveNode.bind(this))
|
||||
.subscribe(
|
||||
node => this.node = node,
|
||||
error => this.handleError(error)
|
||||
error => this.logService.error(error)
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.recalculateProperties();
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const nodeChange: SimpleChange = changes['node'];
|
||||
if (nodeChange) {
|
||||
const node = nodeChange.currentValue;
|
||||
this.basicProperties$ = this.contentMetadataService.getBasicProperties(node);
|
||||
this.aspects$ = this.contentMetadataService.getAspectProperties(node, this.preset);
|
||||
}
|
||||
}
|
||||
|
||||
private saveNode({ changed: nodeBody }): Observable<MinimalNodeEntryEntity> {
|
||||
return this.nodesApi.updateNode(this.node.id, nodeBody);
|
||||
}
|
||||
|
||||
private handleError(error): void {
|
||||
/*tslint:disable-next-line*/
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
private recalculateProperties(): void {
|
||||
let basicProperties = this.contentMetadataService.getBasicProperties(this.node);
|
||||
|
||||
if (this.maxPropertiesToShow) {
|
||||
basicProperties = basicProperties.slice(0, this.maxPropertiesToShow);
|
||||
}
|
||||
|
||||
this.properties = [...basicProperties];
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
<mat-card *ngIf="node">
|
||||
<mat-card-content>
|
||||
<adf-content-metadata [node]="node" [editable]="editable" [maxPropertiesToShow]="maxPropertiesToShow"></adf-content-metadata>
|
||||
</mat-card-content>
|
||||
<mat-card-footer class="adf-viewer-default-sidebar-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
<div>
|
||||
<button mat-icon-button>
|
||||
<mat-icon>star_border</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="toggleEdit()">
|
||||
<mat-icon>mode_edit</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-button (click)="toggleExpanded()">
|
||||
<ng-container *ngIf="!expanded">
|
||||
<span>{{ 'ADF_VIEWER.SIDEBAR.METADATA.MORE_INFORMATION' | translate }}</span>
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="expanded">
|
||||
<span>{{ 'ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION' | translate }}</span>
|
||||
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||
</ng-container>
|
||||
</button>
|
||||
</mat-card-footer>
|
||||
</mat-card>
|
@@ -1,3 +0,0 @@
|
||||
<div class="adf-metadata-properties">
|
||||
<adf-card-view [properties]="properties" [editable]="editable"></adf-card-view>
|
||||
</div>
|
@@ -1,29 +0,0 @@
|
||||
@mixin adf-content-metadata-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf {
|
||||
&-metadata-properties {
|
||||
|
||||
.adf-property {
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.adf-property-label {
|
||||
display: block;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
color: mat-color($foreground, text, 0.4);
|
||||
}
|
||||
|
||||
.adf-property-value {
|
||||
display: block;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
color: mat-color($foreground, text, 0.87);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
@import './components/content-metadata/content-metadata.component';
|
||||
@import './components/content-metadata-card/content-metadata-card.component';
|
||||
|
||||
@mixin adf-content-metadata-module-theme($theme) {
|
||||
@include adf-content-metadata-theme($theme);
|
||||
@include adf-content-metadata-card-theme($theme);
|
||||
}
|
@@ -20,10 +20,14 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { CardViewModule } from '@alfresco/adf-core';
|
||||
|
||||
import { ContentMetadataComponent } from './content-metadata.component';
|
||||
import { ContentMetadataCardComponent } from './content-metadata-card.component';
|
||||
import { CardViewModule , FileSizePipe } from '@alfresco/adf-core';
|
||||
import { ContentMetadataComponent } from './components/content-metadata/content-metadata.component';
|
||||
import { ContentMetadataCardComponent } from './components/content-metadata-card/content-metadata-card.component';
|
||||
import { PropertyDescriptorsService } from './services/property-descriptors.service';
|
||||
import { PropertyDescriptorLoaderService } from './services/properties-loader.service';
|
||||
import { AspectWhiteListService } from './services/aspect-whitelist.service';
|
||||
import { BasicPropertiesService } from './services/basic-properties.service';
|
||||
import { ContentMetadataService } from './services/content-metadata.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -40,6 +44,14 @@ import { ContentMetadataCardComponent } from './content-metadata-card.component'
|
||||
declarations: [
|
||||
ContentMetadataComponent,
|
||||
ContentMetadataCardComponent
|
||||
],
|
||||
providers: [
|
||||
ContentMetadataService,
|
||||
PropertyDescriptorsService,
|
||||
PropertyDescriptorLoaderService,
|
||||
AspectWhiteListService,
|
||||
BasicPropertiesService,
|
||||
FileSizePipe
|
||||
]
|
||||
})
|
||||
export class ContentMetadataModule {}
|
||||
|
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export interface AspectProperty {
|
||||
name: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
dataType: string;
|
||||
defaultValue?: any;
|
||||
mandatory: boolean;
|
||||
multiValued: boolean;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { AspectProperty } from "./aspect-property.interface";
|
||||
|
||||
export interface Aspect {
|
||||
name: string;
|
||||
title: string;
|
||||
description: string;
|
||||
properties: AspectProperty[]
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItem} from '@alfresco/adf-core';
|
||||
|
||||
export interface CardViewAspect {
|
||||
name: string;
|
||||
properties: CardViewItem[]
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export * from './aspect-property.interface';
|
||||
export * from './aspect.interface';
|
||||
export * from './card-view-aspect.interface';
|
@@ -15,5 +15,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './content-metadata.component';
|
||||
export * from './content-metadata.service';
|
||||
export * from './components/content-metadata-card/content-metadata-card.component';
|
||||
|
@@ -0,0 +1,75 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
||||
|
||||
@Injectable()
|
||||
export class AspectWhiteListService {
|
||||
|
||||
static readonly DEFAULT_PRESET = '*';
|
||||
static readonly DEFAULT_PRESET_NAME = 'default';
|
||||
|
||||
preset: object | string = AspectWhiteListService.DEFAULT_PRESET;
|
||||
|
||||
constructor(private appConfigService: AppConfigService,
|
||||
private logService: LogService) {}
|
||||
|
||||
public choosePreset(presetName: string) {
|
||||
try {
|
||||
const preset = this.appConfigService.config['content-metadata'].presets[presetName];
|
||||
|
||||
if (preset) {
|
||||
this.preset = preset;
|
||||
} else if (presetName !== AspectWhiteListService.DEFAULT_PRESET_NAME) {
|
||||
this.logService.error(`No content-metadata preset for: ${presetName}`);
|
||||
}
|
||||
} catch (e) {
|
||||
this.preset = AspectWhiteListService.DEFAULT_PRESET;
|
||||
}
|
||||
}
|
||||
|
||||
public isAspectAllowed(aspectName) {
|
||||
if (this.isEveryAspectAllowed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const aspectNames = Object.keys(this.preset);
|
||||
return aspectNames.indexOf(aspectName) !== -1;
|
||||
}
|
||||
|
||||
public isPropertyAllowed(aspectName, propertyName) {
|
||||
if (this.isEveryAspectAllowed || this.isEveryPropertyAllowedFor(aspectName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.preset[aspectName]) {
|
||||
return this.preset[aspectName].indexOf(propertyName) !== -1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private get isEveryAspectAllowed(): boolean {
|
||||
return typeof this.preset === 'string' && this.preset === AspectWhiteListService.DEFAULT_PRESET;
|
||||
}
|
||||
|
||||
private isEveryPropertyAllowedFor(aspectName): boolean {
|
||||
const whitedListedProperties = this.preset[aspectName];
|
||||
return typeof whitedListedProperties === 'string' && whitedListedProperties === AspectWhiteListService.DEFAULT_PRESET;
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { CardViewDateItemModel, CardViewTextItemModel, FileSizePipe } from '@alfresco/adf-core';
|
||||
|
||||
@Injectable()
|
||||
export class ContentMetadataService {
|
||||
export class BasicPropertiesService {
|
||||
|
||||
constructor(private fileSizePipe: FileSizePipe) {}
|
||||
|
@@ -0,0 +1,325 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { ContentMetadataService } from './content-metadata.service';
|
||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||
import { BasicPropertiesService } from './basic-properties.service';
|
||||
import { AspectWhiteListService } from './aspect-whitelist.service';
|
||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Aspect, AspectProperty } from '../interfaces/content-metadata.interfaces';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import {
|
||||
CardViewTextItemModel,
|
||||
CardViewDateItemModel,
|
||||
CardViewIntItemModel,
|
||||
CardViewFloatItemModel,
|
||||
LogService,
|
||||
CardViewBoolItemModel,
|
||||
CardViewDatetimeItemModel
|
||||
} from '@alfresco/adf-core';
|
||||
|
||||
describe('ContentMetadataService', () => {
|
||||
|
||||
let service: ContentMetadataService,
|
||||
descriptorsService: PropertyDescriptorsService,
|
||||
aspects: Aspect[],
|
||||
aspect: Aspect,
|
||||
aspectProperty: AspectProperty,
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
const dummyPreset = 'default';
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ContentMetadataService,
|
||||
BasicPropertiesService,
|
||||
AspectWhiteListService,
|
||||
PropertyDescriptorLoaderService,
|
||||
AlfrescoApiService,
|
||||
{ provide: LogService, useValue: { error: () => {} }},
|
||||
PropertyDescriptorsService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.get(ContentMetadataService);
|
||||
descriptorsService = TestBed.get(PropertyDescriptorsService);
|
||||
|
||||
node = <MinimalNodeEntryEntity> { properties: {} };
|
||||
aspectProperty = {
|
||||
name: 'FAS:PLAGUE',
|
||||
title: 'The Faro Plague',
|
||||
dataType: '',
|
||||
defaultValue: '',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
};
|
||||
aspect = {
|
||||
name: 'FAS:FAS',
|
||||
title: 'Faro Automated Solutions',
|
||||
description: 'Faro Automated Solutions is an old Earth corporation that manufactured robots in the mid-21st century.',
|
||||
properties: [aspectProperty]
|
||||
};
|
||||
aspects = [];
|
||||
spyOn(descriptorsService, 'getAspects').and.returnValue(Observable.of(aspects));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('General transformation', () => {
|
||||
|
||||
it('should translate more properties in an aspect properly', () => {
|
||||
aspect.properties = [{
|
||||
name: 'FAS:PLAGUE',
|
||||
title: 'title',
|
||||
dataType: 'd:text',
|
||||
defaultValue: 'defaultValue',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
},
|
||||
{
|
||||
name: 'FAS:ALOY',
|
||||
title: 'title',
|
||||
dataType: 'd:text',
|
||||
defaultValue: 'defaultValue',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
}];
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
expect(cardViewAspect[0].properties.length).toBe(2);
|
||||
expect(cardViewAspect[0].properties[0] instanceof CardViewTextItemModel).toBeTruthy('First property should be instance of CardViewTextItemModel');
|
||||
expect(cardViewAspect[0].properties[1] instanceof CardViewTextItemModel).toBeTruthy('Second property should be instance of CardViewTextItemModel');
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate every property in every aspect properly', () => {
|
||||
aspects.push(
|
||||
Object.assign({}, aspect, {
|
||||
properties: [{
|
||||
name: 'FAS:PLAGUE',
|
||||
title: 'title',
|
||||
dataType: 'd:text',
|
||||
defaultValue: 'defaultvalue',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
}]
|
||||
}),
|
||||
Object.assign({}, aspect, {
|
||||
properties: [{
|
||||
name: 'FAS:ALOY',
|
||||
title: 'title',
|
||||
dataType: 'd:text',
|
||||
defaultValue: 'defaultvalue',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
expect(cardViewAspect.length).toBe(2);
|
||||
expect(cardViewAspect[0].properties[0] instanceof CardViewTextItemModel).toBeTruthy('First aspect\'s property should be instance of CardViewTextItemModel');
|
||||
expect(cardViewAspect[1].properties[0] instanceof CardViewTextItemModel).toBeTruthy('Second aspect\'s property should be instance of CardViewTextItemModel');
|
||||
});
|
||||
});
|
||||
|
||||
it('should log an error if unrecognised type is found', () => {
|
||||
const logService = TestBed.get(LogService);
|
||||
spyOn(logService, 'error').and.stub();
|
||||
|
||||
aspectProperty.name = 'FAS:PLAGUE';
|
||||
aspectProperty.title = 'The Faro Plague';
|
||||
aspectProperty.dataType = 'daemonic:scorcher';
|
||||
aspectProperty.defaultValue = 'Daemonic beast';
|
||||
aspects.push(aspect);
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
expect(logService.error).toHaveBeenCalledWith('Unknown type for mapping: daemonic:scorcher');
|
||||
});
|
||||
});
|
||||
|
||||
it('should fall back to singleline property type if unrecognised type is found', () => {
|
||||
aspectProperty.name = 'FAS:PLAGUE';
|
||||
aspectProperty.title = 'The Faro Plague';
|
||||
aspectProperty.dataType = 'daemonic:scorcher';
|
||||
aspectProperty.defaultValue = 'Daemonic beast';
|
||||
aspects.push(aspect);
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewTextItemModel = <CardViewTextItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Different types\'s attributes', () => {
|
||||
|
||||
ContentMetadataService.RECOGNISED_ECM_TYPES.forEach((dataType) => {
|
||||
it(`should translate properly the basic attributes of a property for ${dataType}`, () => {
|
||||
aspectProperty.name = 'prefix:name';
|
||||
aspectProperty.title = 'title';
|
||||
aspectProperty.defaultValue = 'default value';
|
||||
aspectProperty.dataType = dataType;
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'prefix:name': null };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property = cardViewAspect[0].properties[0];
|
||||
expect(property.label).toBe(aspectProperty.title);
|
||||
expect(property.key).toBe('properties.prefix:name');
|
||||
expect(property.default).toBe(aspectProperty.defaultValue);
|
||||
expect(property.editable).toBeTruthy('Property should be editable');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the multiline and value attributes for d:text', () => {
|
||||
aspectProperty.dataType = 'd:text';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewTextItemModel = <CardViewTextItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
||||
expect(property.value).toBe('The Chariot Line');
|
||||
expect(property.multiline).toBeFalsy('Property should be singleline');
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the multiline and value attributes for d:mltext', () => {
|
||||
aspectProperty.dataType = 'd:mltext';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewTextItemModel = <CardViewTextItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewTextItemModel).toBeTruthy('Property should be instance of CardViewTextItemModel');
|
||||
expect(property.value).toBe('The Chariot Line');
|
||||
expect(property.multiline).toBeTruthy('Property should be multiline');
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:date', () => {
|
||||
const expectedValue = new Date().toISOString();
|
||||
aspectProperty.dataType = 'd:date';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': expectedValue };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewDateItemModel = <CardViewDateItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewDateItemModel).toBeTruthy('Property should be instance of CardViewDateItemModel');
|
||||
expect(property.value).toBe(expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:date', () => {
|
||||
const expectedValue = new Date().toISOString();
|
||||
aspectProperty.dataType = 'd:datetime';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': expectedValue };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewDatetimeItemModel = <CardViewDatetimeItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewDatetimeItemModel).toBeTruthy('Property should be instance of CardViewDatetimeItemModel');
|
||||
expect(property.value).toBe(expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:int', () => {
|
||||
aspectProperty.dataType = 'd:int';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': '1024' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewIntItemModel = <CardViewIntItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewIntItemModel).toBeTruthy('Property should be instance of CardViewIntItemModel');
|
||||
expect(property.value).toBe(1024);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:long', () => {
|
||||
aspectProperty.dataType = 'd:long';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': '1024' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewIntItemModel = <CardViewIntItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewIntItemModel).toBeTruthy('Property should be instance of CardViewIntItemModel');
|
||||
expect(property.value).toBe(1024);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:float', () => {
|
||||
aspectProperty.dataType = 'd:float';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': '1024.24' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewFloatItemModel = <CardViewFloatItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewFloatItemModel).toBeTruthy('Property should be instance of CardViewFloatItemModel');
|
||||
expect(property.value).toBe(1024.24);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:double', () => {
|
||||
aspectProperty.dataType = 'd:double';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': '1024.24' };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewFloatItemModel = <CardViewFloatItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewFloatItemModel).toBeTruthy('Property should be instance of CardViewFloatItemModel');
|
||||
expect(property.value).toBe(1024.24);
|
||||
});
|
||||
});
|
||||
|
||||
it('should translate properly the value attribute for d:boolean', () => {
|
||||
aspectProperty.dataType = 'd:boolean';
|
||||
aspects.push(aspect);
|
||||
|
||||
node.properties = { 'FAS:PLAGUE': true };
|
||||
|
||||
service.getAspectProperties(node, dummyPreset).subscribe((cardViewAspect) => {
|
||||
const property: CardViewBoolItemModel = <CardViewBoolItemModel> cardViewAspect[0].properties[0];
|
||||
expect(property instanceof CardViewBoolItemModel).toBeTruthy('Property should be instance of CardViewBoolItemModel');
|
||||
expect(property.value).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,139 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||
import { BasicPropertiesService } from './basic-properties.service';
|
||||
import {
|
||||
CardViewItemProperties,
|
||||
CardViewItem,
|
||||
CardViewTextItemModel,
|
||||
CardViewBoolItemModel,
|
||||
CardViewDateItemModel,
|
||||
CardViewDatetimeItemModel,
|
||||
CardViewIntItemModel,
|
||||
CardViewFloatItemModel,
|
||||
LogService
|
||||
} from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AspectProperty, CardViewAspect, Aspect } from '../interfaces/content-metadata.interfaces';
|
||||
|
||||
const D_TEXT = 'd:text';
|
||||
const D_MLTEXT = 'd:mltext';
|
||||
const D_DATE = 'd:date';
|
||||
const D_DATETIME = 'd:datetime';
|
||||
const D_INT = 'd:int';
|
||||
const D_LONG = 'd:long';
|
||||
const D_FLOAT = 'd:float';
|
||||
const D_DOUBLE = 'd:double';
|
||||
const D_BOOLEAN = 'd:boolean';
|
||||
|
||||
@Injectable()
|
||||
export class ContentMetadataService {
|
||||
|
||||
static readonly RECOGNISED_ECM_TYPES = [ D_TEXT, D_MLTEXT, D_DATE, D_DATETIME, D_INT, D_LONG , D_FLOAT, D_DOUBLE, D_BOOLEAN ];
|
||||
|
||||
constructor(private basicPropertiesService: BasicPropertiesService,
|
||||
private propertyDescriptorsService: PropertyDescriptorsService,
|
||||
private logService: LogService) {}
|
||||
|
||||
getBasicProperties(node: MinimalNodeEntryEntity): Observable<CardViewItem[]> {
|
||||
return Observable.of(this.basicPropertiesService.getBasicProperties(node));
|
||||
}
|
||||
|
||||
getAspectProperties(node: MinimalNodeEntryEntity, preset: string): Observable<CardViewAspect[]> {
|
||||
return this.propertyDescriptorsService.getAspects(node, preset)
|
||||
.map(aspects => this.translateAspects(aspects, node.properties));
|
||||
}
|
||||
|
||||
private translateAspects(aspects: Aspect[], nodeProperties): CardViewAspect[] {
|
||||
return aspects.map(aspect => {
|
||||
const translatedAspect: any = Object.assign({}, aspect);
|
||||
translatedAspect.properties = this.translateProperties(aspect.properties, nodeProperties);
|
||||
return translatedAspect;
|
||||
});
|
||||
}
|
||||
|
||||
private translateProperties(aspectProperties: AspectProperty[], nodeProperties: any): CardViewItem[] {
|
||||
return aspectProperties.map(aspectProperty => {
|
||||
return this.translateProperty(aspectProperty, nodeProperties[aspectProperty.name]);
|
||||
});
|
||||
}
|
||||
|
||||
private translateProperty(aspectProperty: AspectProperty, nodeProperty: any): CardViewItem {
|
||||
this.checkECMTypeValidity(aspectProperty.dataType);
|
||||
|
||||
let propertyDefinition: CardViewItemProperties = {
|
||||
label: aspectProperty.title,
|
||||
value: nodeProperty,
|
||||
key: this.getAspectPropertyKey(aspectProperty.name),
|
||||
default: aspectProperty.defaultValue,
|
||||
editable: true
|
||||
};
|
||||
let property;
|
||||
|
||||
switch (aspectProperty.dataType) {
|
||||
|
||||
case D_MLTEXT:
|
||||
property = new CardViewTextItemModel(Object.assign(propertyDefinition, {
|
||||
multiline: true
|
||||
}));
|
||||
break;
|
||||
|
||||
case D_INT:
|
||||
case D_LONG:
|
||||
property = new CardViewIntItemModel(propertyDefinition);
|
||||
break;
|
||||
|
||||
case D_FLOAT:
|
||||
case D_DOUBLE:
|
||||
property = new CardViewFloatItemModel(propertyDefinition);
|
||||
break;
|
||||
|
||||
case D_DATE:
|
||||
property = new CardViewDateItemModel(propertyDefinition);
|
||||
break;
|
||||
|
||||
case D_DATETIME:
|
||||
property = new CardViewDatetimeItemModel(propertyDefinition);
|
||||
break;
|
||||
|
||||
case D_BOOLEAN:
|
||||
property = new CardViewBoolItemModel(propertyDefinition);
|
||||
break;
|
||||
|
||||
case D_TEXT:
|
||||
default:
|
||||
property = new CardViewTextItemModel(Object.assign(propertyDefinition, {
|
||||
multiline: false
|
||||
}));
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private checkECMTypeValidity(ecmPropertyType) {
|
||||
if (ContentMetadataService.RECOGNISED_ECM_TYPES.indexOf(ecmPropertyType) === -1) {
|
||||
this.logService.error(`Unknown type for mapping: ${ecmPropertyType}`);
|
||||
}
|
||||
}
|
||||
|
||||
private getAspectPropertyKey(propertyName) {
|
||||
return `properties.${propertyName}`;
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ClassesApi } from 'alfresco-js-api';
|
||||
|
||||
describe('PropertyDescriptorLoaderService', () => {
|
||||
|
||||
let aspectProperties: PropertyDescriptorLoaderService,
|
||||
classesApi: ClassesApi;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
PropertyDescriptorLoaderService,
|
||||
AlfrescoApiService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
aspectProperties = TestBed.get(PropertyDescriptorLoaderService);
|
||||
const alfrescoApiService = TestBed.get(AlfrescoApiService);
|
||||
classesApi = alfrescoApiService.classesApi;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should load the aspects passed by paramter', () => {
|
||||
spyOn(classesApi, 'getClass');
|
||||
|
||||
aspectProperties.load(['exif:exif', 'cm:content', 'custom:custom'])
|
||||
.subscribe(() => {});
|
||||
|
||||
expect(classesApi.getClass).toHaveBeenCalledTimes(3);
|
||||
expect(classesApi.getClass).toHaveBeenCalledWith('exif_exif');
|
||||
expect(classesApi.getClass).toHaveBeenCalledWith('cm_content');
|
||||
expect(classesApi.getClass).toHaveBeenCalledWith('custom_custom');
|
||||
});
|
||||
|
||||
it('should merge the forked values', (done) => {
|
||||
|
||||
const exifResponse = {
|
||||
name: 'exif:exif',
|
||||
id: 1,
|
||||
properties: {
|
||||
'exif:1': { id: 'exif:1:id', name: 'exif:1' },
|
||||
'exif:2': { id: 'exif:2:id', name: 'exif:2' }
|
||||
}
|
||||
};
|
||||
|
||||
const contentResponse = {
|
||||
name: 'cm:content',
|
||||
id: 2,
|
||||
properties: {
|
||||
'cm:content': { id: 'cm:content:id', name: 'cm:content' }
|
||||
}
|
||||
};
|
||||
|
||||
const apiResponses = [ exifResponse, contentResponse ];
|
||||
let counter = 0;
|
||||
|
||||
spyOn(classesApi, 'getClass').and.callFake(() => {
|
||||
return Observable.of(apiResponses[counter++]);
|
||||
});
|
||||
|
||||
aspectProperties.load(['exif:exif', 'cm:content'])
|
||||
.subscribe({
|
||||
next: (data) => {
|
||||
expect(data[0]).toBe(exifResponse);
|
||||
expect(data[1]).toBe(contentResponse);
|
||||
},
|
||||
complete: done
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,36 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { forkJoin } from 'rxjs/observable/forkJoin';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { defer } from 'rxjs/observable/defer';
|
||||
|
||||
@Injectable()
|
||||
export class PropertyDescriptorLoaderService {
|
||||
|
||||
constructor(private alfrescoApiService: AlfrescoApiService) {}
|
||||
|
||||
load(aspects: string[]): Observable<any> {
|
||||
const aspectFetchStreams = aspects
|
||||
.map(aspectName => aspectName.replace(':', '_'))
|
||||
.map(aspectName => defer( () => this.alfrescoApiService.classesApi.getClass(aspectName)) );
|
||||
|
||||
return forkJoin(aspectFetchStreams);
|
||||
}
|
||||
}
|
@@ -0,0 +1,209 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { AspectWhiteListService } from './aspect-whitelist.service';
|
||||
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AspectProperty } from '../interfaces/content-metadata.interfaces';
|
||||
|
||||
describe('PropertyDescriptorsService', () => {
|
||||
|
||||
let contentMetadataService: PropertyDescriptorsService,
|
||||
aspectProperties: PropertyDescriptorLoaderService,
|
||||
appConfigService: AppConfigService,
|
||||
logService: LogService,
|
||||
node: MinimalNodeEntryEntity,
|
||||
testPresets: any;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
PropertyDescriptorsService,
|
||||
PropertyDescriptorLoaderService,
|
||||
AppConfigService,
|
||||
AspectWhiteListService,
|
||||
{ provide: LogService, useValue: { error: () => {} }},
|
||||
AlfrescoApiService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
contentMetadataService = TestBed.get(PropertyDescriptorsService);
|
||||
aspectProperties = TestBed.get(PropertyDescriptorLoaderService);
|
||||
appConfigService = TestBed.get(AppConfigService);
|
||||
logService = TestBed.get(LogService);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('getAspects', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
node = <MinimalNodeEntryEntity> { aspectNames: [ 'exif:exif', 'cm:content', 'custom:custom' ] };
|
||||
|
||||
testPresets = {};
|
||||
appConfigService.config['content-metadata'] = {
|
||||
presets: testPresets
|
||||
};
|
||||
});
|
||||
|
||||
it('should call the aspect properties loading for the default aspects related to the given node and defined in the app config', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
||||
testPresets.default = { 'exif:exif': [], 'custom:custom': [], 'banana:banana': [] };
|
||||
|
||||
contentMetadataService.getAspects(node);
|
||||
|
||||
expect(aspectProperties.load).toHaveBeenCalledWith(['exif:exif', 'custom:custom']);
|
||||
});
|
||||
|
||||
it('should call the aspect properties loading for the defined aspects related to the given node and defined in the app config', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
||||
testPresets.pink = { 'cm:content': [], 'custom:custom': [] };
|
||||
|
||||
contentMetadataService.getAspects(node, 'pink');
|
||||
|
||||
expect(aspectProperties.load).toHaveBeenCalledWith(['cm:content', 'custom:custom']);
|
||||
});
|
||||
|
||||
it('should call the aspect properties loading for all the node aspectNames if the "*" widecard is used for the preset', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
||||
testPresets.default = '*';
|
||||
|
||||
contentMetadataService.getAspects(node);
|
||||
|
||||
expect(aspectProperties.load).toHaveBeenCalledWith(['exif:exif', 'cm:content', 'custom:custom']);
|
||||
});
|
||||
|
||||
it('should call the aspect properties loading for all the node aspectNames if there is no preset data defined in the app config', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
||||
spyOn(logService, 'error').and.stub();
|
||||
appConfigService.config['content-metadata'] = undefined;
|
||||
|
||||
contentMetadataService.getAspects(node);
|
||||
|
||||
expect(logService.error).not.toHaveBeenCalled();
|
||||
expect(aspectProperties.load).toHaveBeenCalledWith(['exif:exif', 'cm:content', 'custom:custom']);
|
||||
});
|
||||
|
||||
it('should show meaningful error when invalid preset are given', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(x => Observable.of({}));
|
||||
spyOn(logService, 'error').and.stub();
|
||||
testPresets.pink = { 'cm:content': {}, 'custom:custom': {} };
|
||||
|
||||
contentMetadataService.getAspects(node, 'blue');
|
||||
|
||||
expect(logService.error).toHaveBeenCalledWith('No content-metadata preset for: blue');
|
||||
});
|
||||
|
||||
it('should filter out properties which are not defined in the particular aspect', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(() => {
|
||||
return Observable.of([
|
||||
{
|
||||
name: 'exif:exif',
|
||||
properties: [
|
||||
{ name: 'exif:1' },
|
||||
{ name: 'exif:2' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
testPresets.default = { 'exif:exif': ['exif:2'] };
|
||||
|
||||
contentMetadataService.getAspects(node).subscribe({
|
||||
next: (aspects) => {
|
||||
expect(aspects[0].name).toBe('exif:exif');
|
||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:2' });
|
||||
expect(aspects[0].properties).not.toContain(<AspectProperty> { name: 'exif:1' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept "*" wildcard for aspect properties', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(() => {
|
||||
return Observable.of([
|
||||
{
|
||||
name: 'exif:exif',
|
||||
properties: [
|
||||
{ name: 'exif:1' },
|
||||
{ name: 'exif:2' }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'custom:custom',
|
||||
properties: [
|
||||
{ name: 'custom:1' },
|
||||
{ name: 'custom:2' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
testPresets.default = {
|
||||
'exif:exif': '*',
|
||||
'custom:custom': ['custom:1']
|
||||
};
|
||||
|
||||
contentMetadataService.getAspects(node).subscribe({
|
||||
next: (aspects) => {
|
||||
expect(aspects.length).toBe(2);
|
||||
expect(aspects[0].name).toBe('exif:exif');
|
||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:1' });
|
||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:2' });
|
||||
|
||||
expect(aspects[1].name).toBe('custom:custom');
|
||||
expect(aspects[1].properties).toContain(<AspectProperty> { name: 'custom:1' });
|
||||
expect(aspects[1].properties).not.toContain(<AspectProperty> { name: 'custom:2' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter out aspects which are not present in app config preset', () => {
|
||||
spyOn(aspectProperties, 'load').and.callFake(() => {
|
||||
return Observable.of([
|
||||
{
|
||||
name: 'exif:exif',
|
||||
properties: [
|
||||
{ name: 'exif:1' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
testPresets.default = {
|
||||
'exif:exif': ['exif:1'],
|
||||
'banana:banana': ['banana:1']
|
||||
};
|
||||
|
||||
contentMetadataService.getAspects(node).subscribe({
|
||||
next: (aspects) => {
|
||||
expect(aspects.length).toBe(1);
|
||||
expect(aspects[0].name).toBe('exif:exif');
|
||||
expect(aspects[0].properties).toContain(<AspectProperty> { name: 'exif:1' });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,60 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { PropertyDescriptorLoaderService } from './properties-loader.service';
|
||||
import { AspectWhiteListService } from './aspect-whitelist.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Aspect, AspectProperty } from '../interfaces/content-metadata.interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class PropertyDescriptorsService {
|
||||
|
||||
constructor(private aspectWhiteListService: AspectWhiteListService,
|
||||
private aspectPropertiesService: PropertyDescriptorLoaderService) {}
|
||||
|
||||
getAspects(node: MinimalNodeEntryEntity, presetName: string = 'default'): Observable<Aspect[]> {
|
||||
this.aspectWhiteListService.choosePreset(presetName);
|
||||
|
||||
return this.loadAspectDescriptors(node.aspectNames)
|
||||
.map(this.filterPropertiesByWhitelist.bind(this));
|
||||
}
|
||||
|
||||
private loadAspectDescriptors(aspectsAssignedToNode: string[]): Observable<any> {
|
||||
const aspectsToLoad = aspectsAssignedToNode
|
||||
.filter(nodeAspectName => this.aspectWhiteListService.isAspectAllowed(nodeAspectName));
|
||||
|
||||
return this.aspectPropertiesService.load(aspectsToLoad);
|
||||
}
|
||||
|
||||
private filterPropertiesByWhitelist(aspectDescriptors): Aspect[] {
|
||||
return aspectDescriptors.map((aspectDescriptor) => {
|
||||
return Object.assign({}, aspectDescriptor, {
|
||||
properties: this.getFilteredPropertiesArray(aspectDescriptor)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getFilteredPropertiesArray(aspectDescriptor): AspectProperty[] {
|
||||
const aspectName = aspectDescriptor.name;
|
||||
|
||||
return Object.keys(aspectDescriptor.properties)
|
||||
.map(propertyName => aspectDescriptor.properties[propertyName])
|
||||
.filter(property => this.aspectWhiteListService.isPropertyAllowed(aspectName, property.name));
|
||||
}
|
||||
}
|
@@ -188,6 +188,7 @@
|
||||
},
|
||||
"METADATA": {
|
||||
"BASIC": {
|
||||
"HEADER": "Properties",
|
||||
"NAME": "Name",
|
||||
"TITLE": "Title",
|
||||
"DESCRIPTION": "Description",
|
||||
|
@@ -29,6 +29,7 @@ import {
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatRippleModule,
|
||||
MatExpansionModule,
|
||||
MatSelectModule
|
||||
} from '@angular/material';
|
||||
|
||||
@@ -46,6 +47,7 @@ export function modules() {
|
||||
MatRippleModule,
|
||||
MatMenuModule,
|
||||
MatOptionModule,
|
||||
MatExpansionModule,
|
||||
MatSelectModule
|
||||
];
|
||||
}
|
||||
|
@@ -12,9 +12,8 @@
|
||||
|
||||
@import '../dialogs/folder.dialog';
|
||||
|
||||
@import '../content-metadata/content-metadata.component';
|
||||
@import '../content-metadata/content-metadata-card.component';
|
||||
@import '../content-node-selector/content-node-selector.component';
|
||||
@import '../content-metadata/content-metadata.module';
|
||||
|
||||
@mixin adf-content-services-theme($theme) {
|
||||
@include adf-breadcrumb-theme($theme);
|
||||
@@ -27,7 +26,6 @@
|
||||
@include adf-search-control-theme($theme);
|
||||
@include adf-search-autocomplete-theme($theme);
|
||||
@include adf-dialog-theme($theme);
|
||||
@include adf-content-metadata-theme($theme);
|
||||
@include adf-content-metadata-card-theme($theme);
|
||||
@include adf-content-node-selector-dialog-theme($theme) ;
|
||||
@include adf-content-metadata-module-theme($theme);
|
||||
}
|
||||
|
@@ -1,30 +0,0 @@
|
||||
<div class="adf-property-label">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-value">
|
||||
<span *ngIf="!isEditable()">
|
||||
<span [attr.data-automation-id]="'card-dateitem-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span *ngIf="isEditable()" class="adf-dateitem-editable">
|
||||
<input class="adf-invisible-date-input" [matDatepicker]="picker" [value]="valueDate"
|
||||
(dateChange)="onDateChanged($event)">
|
||||
<span
|
||||
class="adf-datepicker-toggle"
|
||||
[attr.data-automation-id]="'datepicker-label-toggle-' + property.key"
|
||||
(click)="showDatePicker($event)">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<mat-datepicker-toggle
|
||||
[attr.data-automation-id]="'datepickertoggle-' + property.key"
|
||||
[for]="picker">
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #picker
|
||||
[attr.data-automation-id]="'datepicker-' + property.key"
|
||||
[startAt]="valueDate"
|
||||
[touchUi]="true">
|
||||
</mat-datepicker>
|
||||
</span>
|
||||
<ng-template #elseEmptyValueBlock>
|
||||
{{ property.default | translate }}
|
||||
</ng-template>
|
||||
</div>
|
@@ -1,36 +0,0 @@
|
||||
@mixin adf-card-view-dateitem-theme($theme) {
|
||||
|
||||
.adf {
|
||||
&-invisible-date-input {
|
||||
height: 24px;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-dateitem-editable {
|
||||
cursor: pointer;
|
||||
|
||||
button.mat-icon-button {
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.5;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
&:hover mat-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
<div class="adf-property-label">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-value">
|
||||
<span *ngIf="!isEditable()">
|
||||
<span *ngIf="!isClickable(); else elseBlock" [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<ng-template #elseBlock>
|
||||
<span class="adf-textitem-clickable-value" (click)="clicked()" [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
</ng-template>
|
||||
</span>
|
||||
<span *ngIf="isEditable()">
|
||||
<div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<span [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<mat-icon fxFlex="0 0 auto" [attr.data-automation-id]="'card-textitem-edit-icon-' + property.key" class="adf-textitem-icon">create</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="inEdit" class="adf-textitem-editable">
|
||||
<mat-form-field floatPlaceholder="never" class="adf-input-container">
|
||||
<input *ngIf="!property.multiline" #editorInput
|
||||
matInput
|
||||
class="adf-input"
|
||||
[placeholder]="property.default | translate"
|
||||
[(ngModel)]="editedValue"
|
||||
[attr.data-automation-id]="'card-textitem-editinput-' + property.key">
|
||||
<textarea *ngIf="property.multiline" #editorInput
|
||||
matInput
|
||||
matTextareaAutosize
|
||||
matAutosizeMaxRows="1"
|
||||
matAutosizeMaxRows="5"
|
||||
class="adf-textarea"
|
||||
[placeholder]="property.default | translate"
|
||||
[(ngModel)]="editedValue"
|
||||
[attr.data-automation-id]="'card-textitem-edittextarea-' + property.key"></textarea>
|
||||
</mat-form-field>
|
||||
<mat-icon
|
||||
class="adf-textitem-icon adf-update-icon"
|
||||
(click)="update()"
|
||||
[attr.data-automation-id]="'card-textitem-update-' + property.key">done</mat-icon>
|
||||
<mat-icon
|
||||
class="adf-textitem-icon adf-reset-icon"
|
||||
(click)="reset()"
|
||||
[attr.data-automation-id]="'card-textitem-reset-' + property.key">clear</mat-icon>
|
||||
</div>
|
||||
</span>
|
||||
<ng-template #elseEmptyValueBlock>
|
||||
{{ property.default | translate }}
|
||||
</ng-template>
|
||||
</div>
|
@@ -1,233 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatDatepickerModule, MatIconModule, MatInputModule, MatNativeDateModule } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { CardViewTextItemModel } from '../models/card-view-textitem.model';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { LogService } from '../services/log.service';
|
||||
import { TranslateLoaderService } from '../services/translate-loader.service';
|
||||
|
||||
import { CardViewTextItemComponent } from './card-view-textitem.component';
|
||||
|
||||
describe('CardViewTextItemComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<CardViewTextItemComponent>;
|
||||
let component: CardViewTextItemComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NoopAnimationsModule,
|
||||
MatDatepickerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatNativeDateModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderService
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
CardViewTextItemComponent
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
CardViewUpdateService,
|
||||
LogService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CardViewTextItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: 'Lorem ipsum',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: false
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should render the label and value', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let labelValue = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(labelValue).not.toBeNull();
|
||||
expect(labelValue.nativeElement.innerText).toBe('Text label');
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('Lorem ipsum');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and editable false', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: false
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and editable true', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: true
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and clickable false', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
clickable: false
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and clickable true', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
clickable: true
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render value when editable:true', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('Lorem ipsum');
|
||||
});
|
||||
|
||||
it('should render the edit icon in case of editable:true', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-${component.property.key}"]`));
|
||||
expect(editIcon).not.toBeNull('Edit icon should be shown');
|
||||
});
|
||||
|
||||
it('should NOT render the edit icon in case of editable:false', () => {
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-${component.property.key}"]`));
|
||||
expect(editIcon).toBeNull('Edit icon should NOT be shown');
|
||||
});
|
||||
|
||||
it('should NOT render the picker and toggle in case of editable:true but (general) editable:false', () => {
|
||||
component.editable = false;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-${component.property.key}"]`));
|
||||
expect(editIcon).toBeNull('Edit icon should NOT be shown');
|
||||
});
|
||||
|
||||
it('should trigger an update event on the CardViewUpdateService', (done) => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
const expectedText = 'changed text';
|
||||
fixture.detectChanges();
|
||||
|
||||
cardViewUpdateService.itemUpdated$.subscribe(
|
||||
(updateNotification) => {
|
||||
expect(updateNotification.target).toBe(component.property);
|
||||
expect(updateNotification.changed).toEqual({ textkey: expectedText });
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-toggle-${component.property.key}"]`));
|
||||
editIcon.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
let editInput = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-editinput-${component.property.key}"]`));
|
||||
editInput.nativeElement.value = expectedText;
|
||||
editInput.nativeElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
|
||||
let updateInput = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-update-${component.property.key}"]`));
|
||||
updateInput.triggerEventHandler('click', null);
|
||||
});
|
||||
|
||||
it('should switch back to readonly mode after an update attempt', async(() => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.inEdit = true;
|
||||
component.editedValue = 'updated-value';
|
||||
fixture.detectChanges();
|
||||
|
||||
component.update();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.property.value).toBe(component.editedValue);
|
||||
expect(component.inEdit).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
});
|
@@ -1,8 +0,0 @@
|
||||
<div class="adf-property-list">
|
||||
<div *ngFor="let property of properties">
|
||||
<div [attr.data-automation-id]="'header-'+property.key" class="adf-property">
|
||||
<adf-card-view-item-dispatcher [property]="property" [editable]="editable"></adf-card-view-item-dispatcher>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,34 +0,0 @@
|
||||
|
||||
@mixin adf-card-view-theme($theme) {
|
||||
|
||||
.adf {
|
||||
&-property-list {
|
||||
display: table;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
&-property {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
&-property &-property-label {
|
||||
display: table-cell;
|
||||
min-width: 100px;
|
||||
padding-right: 30px;
|
||||
word-wrap: break-word;
|
||||
color: rgb(186, 186, 186);;
|
||||
vertical-align: top;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
&-property &-property-value {
|
||||
width: 100%;
|
||||
display: table-cell;
|
||||
color: rgb(101, 101, 101);
|
||||
vertical-align: top;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
11
lib/core/card-view/card-view.module.scss
Normal file
11
lib/core/card-view/card-view.module.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
@import './components/card-view-dateitem/card-view-dateitem.component';
|
||||
@import './components/card-view-textitem/card-view-textitem.component';
|
||||
@import './components/card-view/card-view.component';
|
||||
@import '~@mat-datetimepicker/core/datetimepicker/datetimepicker-theme.scss';
|
||||
|
||||
@mixin adf-card-view-module-theme($theme) {
|
||||
@include adf-card-view-dateitem-theme($theme);
|
||||
@include adf-card-view-textitem-theme($theme);
|
||||
@include adf-card-view-theme($theme);
|
||||
@include mat-datetimepicker-theme($theme);
|
||||
}
|
@@ -18,47 +18,69 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule, MatDatepickerModule, MatIconModule, MatInputModule, MatNativeDateModule } from '@angular/material';
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatDatepickerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatCheckboxModule,
|
||||
MatNativeDateModule
|
||||
} from '@angular/material';
|
||||
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CardViewContentProxyDirective } from './card-view-content-proxy.directive';
|
||||
import { CardViewDateItemComponent } from './card-view-dateitem.component';
|
||||
import { CardViewItemDispatcherComponent } from './card-view-item-dispatcher.component';
|
||||
import { CardViewMapItemComponent } from './card-view-mapitem.component';
|
||||
import { CardViewTextItemComponent } from './card-view-textitem.component';
|
||||
import { CardViewComponent } from './card-view.component';
|
||||
import { CardViewContentProxyDirective } from './directives/card-view-content-proxy.directive';
|
||||
import { CardViewComponent } from './components/card-view/card-view.component';
|
||||
import { CardViewBoolItemComponent } from './components/card-view-boolitem/card-view-boolitem.component';
|
||||
import { CardViewDateItemComponent } from './components/card-view-dateitem/card-view-dateitem.component';
|
||||
import { CardViewItemDispatcherComponent } from './components/card-view-item-dispatcher/card-view-item-dispatcher.component';
|
||||
import { CardViewMapItemComponent } from './components/card-view-mapitem/card-view-mapitem.component';
|
||||
import { CardViewTextItemComponent } from './components/card-view-textitem/card-view-textitem.component';
|
||||
|
||||
import { CardItemTypeService } from './services/card-item-types.service';
|
||||
import { CardViewUpdateService } from './services/card-view-update.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
FlexLayoutModule,
|
||||
TranslateModule,
|
||||
MatDatepickerModule,
|
||||
MatNativeDateModule,
|
||||
MatCheckboxModule,
|
||||
MatInputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
FormsModule,
|
||||
FlexLayoutModule,
|
||||
TranslateModule
|
||||
MatDatetimepickerModule,
|
||||
MatNativeDatetimeModule
|
||||
],
|
||||
declarations: [
|
||||
CardViewComponent,
|
||||
CardViewItemDispatcherComponent,
|
||||
CardViewContentProxyDirective,
|
||||
CardViewTextItemComponent,
|
||||
CardViewBoolItemComponent,
|
||||
CardViewDateItemComponent,
|
||||
CardViewMapItemComponent,
|
||||
CardViewDateItemComponent
|
||||
CardViewTextItemComponent,
|
||||
CardViewItemDispatcherComponent,
|
||||
CardViewContentProxyDirective
|
||||
],
|
||||
entryComponents: [
|
||||
CardViewTextItemComponent,
|
||||
CardViewBoolItemComponent,
|
||||
CardViewDateItemComponent,
|
||||
CardViewMapItemComponent,
|
||||
CardViewDateItemComponent
|
||||
CardViewTextItemComponent
|
||||
],
|
||||
exports: [
|
||||
CardViewComponent,
|
||||
CardViewTextItemComponent,
|
||||
CardViewBoolItemComponent,
|
||||
CardViewDateItemComponent,
|
||||
CardViewMapItemComponent,
|
||||
CardViewDateItemComponent
|
||||
CardViewTextItemComponent
|
||||
],
|
||||
providers: [
|
||||
CardItemTypeService,
|
||||
CardViewUpdateService
|
||||
]
|
||||
})
|
||||
export class CardViewModule {}
|
||||
|
@@ -0,0 +1,10 @@
|
||||
<ng-container *ngIf="!property.isEmpty() || isEditable()">
|
||||
<div class="adf-property-label">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-value">
|
||||
<mat-checkbox
|
||||
[checked]="property.displayValue"
|
||||
[disabled]="!isEditable()"
|
||||
(change)="changed($event)">
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
@@ -0,0 +1,2 @@
|
||||
.adf {
|
||||
}
|
@@ -0,0 +1,243 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MaterialModule } from '../../../material.module';
|
||||
import { MatCheckboxChange, MatCheckbox } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
import { LogService } from '../../../services/log.service';
|
||||
import { TranslateLoaderService } from '../../../services/translate-loader.service';
|
||||
|
||||
import { CardViewBoolItemComponent } from './card-view-boolitem.component';
|
||||
import { CardViewBoolItemModel } from '../../models/card-view-boolitem.model';
|
||||
|
||||
describe('CardViewBoolItemComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<CardViewBoolItemComponent>;
|
||||
let component: CardViewBoolItemComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NoopAnimationsModule,
|
||||
MaterialModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderService
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
CardViewBoolItemComponent
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
CardViewUpdateService,
|
||||
LogService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CardViewBoolItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.property = new CardViewBoolItemModel({
|
||||
label: 'Boolean label',
|
||||
value: true,
|
||||
key: 'boolkey',
|
||||
default: false,
|
||||
editable: false
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
||||
it('should render the label and value if the property is editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let label = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(label).not.toBeNull();
|
||||
expect(label.nativeElement.innerText).toBe('Boolean label');
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
expect(value).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render the label and value if the property is NOT editable and doesn\'t have a proper boolean value set' , () => {
|
||||
component.editable = true;
|
||||
component.property.value = undefined;
|
||||
component.property.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let label = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(label).toBeNull();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
expect(value).toBeNull();
|
||||
});
|
||||
|
||||
it('should render the label and value if the property is NOT editable but has a proper boolean value set' , () => {
|
||||
component.editable = true;
|
||||
component.property.value = false;
|
||||
component.property.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let label = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(label).not.toBeNull();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
expect(value).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should render ticked checkbox if property\'s value is true', () => {
|
||||
component.property.value = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('should render ticked checkbox if property\'s value is not set but default is true and editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.property.value = undefined;
|
||||
component.property.default = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.checked).toBe(true);
|
||||
});
|
||||
|
||||
it('should render unticked checkbox if property\'s value is false', () => {
|
||||
component.property.value = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.checked).toBe(false);
|
||||
});
|
||||
|
||||
it('should render unticked checkbox if property\'s value is not set but default is false and editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.property.value = undefined;
|
||||
component.property.default = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.checked).toBe(false);
|
||||
});
|
||||
|
||||
it('should render enabled checkbox if property and component are both editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.property.value = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.hasAttribute('disabled')).toBe(false);
|
||||
});
|
||||
|
||||
it('should render disabled checkbox if property is not editable', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = false;
|
||||
component.property.value = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.hasAttribute('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('should render disabled checkbox if component is not editable', () => {
|
||||
component.editable = false;
|
||||
component.property.editable = true;
|
||||
component.property.value = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value input[type="checkbox"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.hasAttribute('disabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.property.value = undefined;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should trigger the update event when changing the checkbox', () => {
|
||||
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
spyOn(cardViewUpdateService, 'update');
|
||||
|
||||
component.changed(<MatCheckboxChange> {checked: true});
|
||||
|
||||
expect(cardViewUpdateService.update).toHaveBeenCalledWith(component.property, true);
|
||||
});
|
||||
|
||||
it('should update the propery\'s value after a changed', async(() => {
|
||||
component.property.value = true;
|
||||
|
||||
component.changed(<MatCheckboxChange> {checked: false});
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.property.value).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should trigger an update event on the CardViewUpdateService [integration]', (done) => {
|
||||
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
component.property.value = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
cardViewUpdateService.itemUpdated$.subscribe(
|
||||
(updateNotification) => {
|
||||
expect(updateNotification.target).toBe(component.property);
|
||||
expect(updateNotification.changed).toEqual({ boolkey: true });
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
const labelElement = fixture.debugElement.query(By.directive(MatCheckbox)).nativeElement.querySelector('label');
|
||||
labelElement.click();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,47 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MatCheckboxChange } from '@angular/material';
|
||||
import { CardViewBoolItemModel } from '../../models/card-view-boolitem.model';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-card-view-boolitem',
|
||||
templateUrl: './card-view-boolitem.component.html',
|
||||
styleUrls: ['./card-view-boolitem.component.scss']
|
||||
})
|
||||
|
||||
export class CardViewBoolItemComponent {
|
||||
|
||||
@Input()
|
||||
property: CardViewBoolItemModel;
|
||||
|
||||
@Input()
|
||||
editable: boolean;
|
||||
|
||||
constructor(private cardViewUpdateService: CardViewUpdateService) {}
|
||||
|
||||
isEditable() {
|
||||
return this.editable && this.property.editable;
|
||||
}
|
||||
|
||||
changed(change: MatCheckboxChange) {
|
||||
this.cardViewUpdateService.update(this.property, change.checked );
|
||||
this.property.value = change.checked;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<div class="adf-property-label" *ngIf="showProperty() || isEditable()">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-value">
|
||||
<span *ngIf="!isEditable()">
|
||||
<span [attr.data-automation-id]="'card-dateitem-' + property.key">
|
||||
<span *ngIf="showProperty()">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<div *ngIf="isEditable()" class="adf-dateitem-editable">
|
||||
<div class="adf-dateitem-editable-controls">
|
||||
<span
|
||||
class="adf-datepicker-toggle"
|
||||
[attr.data-automation-id]="'datepicker-label-toggle-' + property.key"
|
||||
(click)="showDatePicker($event)">
|
||||
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<mat-datetimepicker-toggle
|
||||
[attr.data-automation-id]="'datepickertoggle-' + property.key"
|
||||
[for]="datetimePicker">
|
||||
</mat-datetimepicker-toggle>
|
||||
</div>
|
||||
|
||||
<input class="adf-invisible-date-input"
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
[value]="valueDate"
|
||||
(dateChange)="onDateChanged($event)">
|
||||
|
||||
<mat-datetimepicker #datetimePicker
|
||||
[type]="property.type"
|
||||
timeInterval="5"
|
||||
[attr.data-automation-id]="'datepicker-' + property.key"
|
||||
[startAt]="valueDate">
|
||||
</mat-datetimepicker>
|
||||
</div>
|
||||
<ng-template #elseEmptyValueBlock>
|
||||
{{ property.default | translate }}
|
||||
</ng-template>
|
||||
</div>
|
@@ -0,0 +1,41 @@
|
||||
@mixin adf-card-view-dateitem-theme($theme) {
|
||||
|
||||
.adf {
|
||||
&-invisible-date-input {
|
||||
height: 2px;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
&-dateitem-editable {
|
||||
cursor: pointer;
|
||||
|
||||
&-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
button.mat-icon-button {
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover mat-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,13 +18,14 @@
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatDatepickerModule, MatInputModule, MatNativeDateModule } from '@angular/material';
|
||||
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import moment from 'moment-es6';
|
||||
import { AppConfigService } from '../index';
|
||||
import { CardViewDateItemModel } from '../models/card-view-dateitem.model';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { TranslateLoaderService } from '../services/translate-loader.service';
|
||||
import { AppConfigService } from '../../../index';
|
||||
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
import { TranslateLoaderService } from '../../../services/translate-loader.service';
|
||||
|
||||
import { CardViewDateItemComponent } from './card-view-dateitem.component';
|
||||
|
||||
@@ -40,6 +41,8 @@ describe('CardViewDateItemComponent', () => {
|
||||
MatDatepickerModule,
|
||||
MatInputModule,
|
||||
MatNativeDateModule,
|
||||
MatNativeDatetimeModule,
|
||||
MatDatetimepickerModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
@@ -87,7 +90,7 @@ describe('CardViewDateItemComponent', () => {
|
||||
expect(value.nativeElement.innerText.trim()).toBe('Jul 10 2017');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and editable:false', () => {
|
||||
it('should NOT render the default as value if the value is empty, editable:false and displayEmpty is false', () => {
|
||||
component.property = new CardViewDateItemModel ({
|
||||
label: 'Date label',
|
||||
value: '',
|
||||
@@ -96,6 +99,26 @@ describe('CardViewDateItemComponent', () => {
|
||||
format: '',
|
||||
editable: false
|
||||
});
|
||||
component.editable = true;
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty, editable:false and displayEmpty is true', () => {
|
||||
component.property = new CardViewDateItemModel ({
|
||||
label: 'Date label',
|
||||
value: '',
|
||||
key: 'datekey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
format: '',
|
||||
editable: false
|
||||
});
|
||||
component.editable = true;
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
@@ -112,6 +135,7 @@ describe('CardViewDateItemComponent', () => {
|
||||
format: '',
|
||||
editable: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
@@ -161,7 +185,7 @@ describe('CardViewDateItemComponent', () => {
|
||||
expect(datePickerToggle).toBeNull('Datepicker toggle should NOT be shown');
|
||||
});
|
||||
|
||||
it('should open the datetimepicker when clicking on the label', () => {
|
||||
it('should open the dateXXXpicker when clicking on the label', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
@@ -190,4 +214,20 @@ describe('CardViewDateItemComponent', () => {
|
||||
|
||||
component.onDateChanged({value: expectedDate});
|
||||
});
|
||||
|
||||
it('should update the propery\'s value after a succesful update attempt', async(() => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.property.value = null;
|
||||
const expectedDate = moment('Jul 10 2017', 'MMM DD YY');
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onDateChanged({value: expectedDate});
|
||||
|
||||
fixture.whenStable().then(
|
||||
(updateNotification) => {
|
||||
expect(component.property.value).toEqual(expectedDate.toDate());
|
||||
}
|
||||
);
|
||||
}));
|
||||
});
|
@@ -16,15 +16,15 @@
|
||||
*/
|
||||
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDatepicker } from '@angular/material';
|
||||
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material';
|
||||
import { MatDatetimepicker } from '@mat-datetimepicker/core';
|
||||
import moment from 'moment-es6';
|
||||
import { Moment } from 'moment';
|
||||
import { CardViewDateItemModel } from '../models/card-view-dateitem.model';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { UserPreferencesService } from '../services/user-preferences.service';
|
||||
import { MomentDateAdapter } from '../utils/momentDateAdapter';
|
||||
import { MOMENT_DATE_FORMATS } from '../utils/moment-date-formats.model';
|
||||
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
import { UserPreferencesService } from '../../../services/user-preferences.service';
|
||||
import { MomentDateAdapter } from '../../../utils/momentDateAdapter';
|
||||
import { MOMENT_DATE_FORMATS } from '../../../utils/moment-date-formats.model';
|
||||
|
||||
@Component({
|
||||
providers: [
|
||||
@@ -42,10 +42,13 @@ export class CardViewDateItemComponent implements OnInit {
|
||||
property: CardViewDateItemModel;
|
||||
|
||||
@Input()
|
||||
editable: boolean;
|
||||
editable: boolean = false;
|
||||
|
||||
@ViewChild(MatDatepicker)
|
||||
public datepicker: MatDatepicker<any>;
|
||||
@Input()
|
||||
displayEmpty: boolean = true;
|
||||
|
||||
@ViewChild(MatDatetimepicker)
|
||||
public datepicker: MatDatetimepicker<any>;
|
||||
|
||||
valueDate: Moment;
|
||||
|
||||
@@ -64,7 +67,10 @@ export class CardViewDateItemComponent implements OnInit {
|
||||
if (this.property.value) {
|
||||
this.valueDate = moment(this.property.value, this.SHOW_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
showProperty() {
|
||||
return this.displayEmpty || !this.property.isEmpty();
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
@@ -81,6 +87,7 @@ export class CardViewDateItemComponent implements OnInit {
|
||||
if (momentDate.isValid()) {
|
||||
this.valueDate = momentDate;
|
||||
this.cardViewUpdateService.update(this.property, momentDate.toDate());
|
||||
this.property.value = momentDate.toDate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,14 +17,14 @@
|
||||
|
||||
/* tslint:disable:component-selector */
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Component, Input, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { CardItemTypeService } from '../services/card-item-types.service';
|
||||
import { CardViewContentProxyDirective } from './card-view-content-proxy.directive';
|
||||
import { CardViewItemDispatcherComponent } from './card-view-item-dispatcher.component';
|
||||
import { CardViewItem } from '../../interfaces/card-view-item.interface';
|
||||
import { CardItemTypeService } from '../../services/card-item-types.service';
|
||||
import { CardViewContentProxyDirective } from '../../directives/card-view-content-proxy.directive';
|
||||
import { CardViewItemDispatcherComponent } from '../card-view-item-dispatcher/card-view-item-dispatcher.component';
|
||||
|
||||
@Component({
|
||||
selector: 'whatever-you-want-to-have',
|
||||
@@ -79,9 +79,10 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
}
|
||||
};
|
||||
component.editable = true;
|
||||
component.displayEmpty = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges(null);
|
||||
component.ngOnChanges({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -97,9 +98,9 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
});
|
||||
|
||||
it('should load the CardViewShinyCustomElementItemComponent only ONCE', () => {
|
||||
component.ngOnChanges();
|
||||
component.ngOnChanges();
|
||||
component.ngOnChanges();
|
||||
component.ngOnChanges({});
|
||||
component.ngOnChanges({});
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
|
||||
const shinyCustomElementItemComponent = fixture.debugElement.queryAll(By.css('whatever-you-want-to-have'));
|
||||
@@ -107,11 +108,32 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
expect(shinyCustomElementItemComponent.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should pass through the property and editable parameters', () => {
|
||||
it('should pass through the property, editable and displayEmpty parameters', () => {
|
||||
const shinyCustomElementItemComponent = fixture.debugElement.query(By.css('whatever-you-want-to-have')).componentInstance;
|
||||
|
||||
expect(shinyCustomElementItemComponent.property).toBe(component.property);
|
||||
expect(shinyCustomElementItemComponent.editable).toBe(component.editable);
|
||||
expect(shinyCustomElementItemComponent.displayEmpty).toBe(component.displayEmpty);
|
||||
});
|
||||
|
||||
it('should update the subcomponent\'s input parameters', () => {
|
||||
const expectedEditable = false,
|
||||
expectedDisplayEmpty = true,
|
||||
expectedProperty = <CardViewItem> {},
|
||||
expectedCustomInput = 1;
|
||||
|
||||
component.ngOnChanges({
|
||||
editable: new SimpleChange(true, expectedEditable, false),
|
||||
displayEmpty: new SimpleChange(false, expectedDisplayEmpty, false),
|
||||
property: new SimpleChange(null, expectedProperty, false),
|
||||
customInput: new SimpleChange(0, expectedCustomInput, false)
|
||||
});
|
||||
|
||||
const shinyCustomElementItemComponent = fixture.debugElement.query(By.css('whatever-you-want-to-have')).componentInstance;
|
||||
expect(shinyCustomElementItemComponent.property).toBe(expectedProperty);
|
||||
expect(shinyCustomElementItemComponent.editable).toBe(expectedEditable);
|
||||
expect(shinyCustomElementItemComponent.displayEmpty).toBe(expectedDisplayEmpty);
|
||||
expect(shinyCustomElementItemComponent.customInput).toBe(expectedCustomInput);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -138,21 +160,21 @@ describe('CardViewItemDispatcherComponent', () => {
|
||||
lifeCycleMethods.forEach((lifeCycleMethod) => {
|
||||
shinyCustomElementItemComponent[lifeCycleMethod] = () => {};
|
||||
spyOn(shinyCustomElementItemComponent, lifeCycleMethod);
|
||||
const param1 = {};
|
||||
const param2 = {};
|
||||
const param = {};
|
||||
|
||||
component[lifeCycleMethod].call(component, param1, param2);
|
||||
component[lifeCycleMethod].call(component, param);
|
||||
|
||||
expect(shinyCustomElementItemComponent[lifeCycleMethod]).toHaveBeenCalledWith(param1, param2);
|
||||
expect(shinyCustomElementItemComponent[lifeCycleMethod]).toHaveBeenCalledWith(param);
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT call through the lifecycle methods if the method does not exist (no error should be thrown)', () => {
|
||||
const param = {};
|
||||
lifeCycleMethods.forEach((lifeCycleMethod) => {
|
||||
shinyCustomElementItemComponent[lifeCycleMethod] = undefined;
|
||||
|
||||
const execution = () => {
|
||||
component[lifeCycleMethod].call(component);
|
||||
component[lifeCycleMethod].call(component, param);
|
||||
};
|
||||
|
||||
expect(execution).not.toThrowError();
|
@@ -20,11 +20,13 @@ import {
|
||||
ComponentFactoryResolver,
|
||||
Input,
|
||||
OnChanges,
|
||||
SimpleChange,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { CardItemTypeService } from '../services/card-item-types.service';
|
||||
import { CardViewContentProxyDirective } from './card-view-content-proxy.directive';
|
||||
import { CardViewItem } from '../../interfaces/card-view-item.interface';
|
||||
import { CardItemTypeService } from '../../services/card-item-types.service';
|
||||
import { CardViewContentProxyDirective } from '../../directives/card-view-content-proxy.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-card-view-item-dispatcher',
|
||||
@@ -37,6 +39,9 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
||||
@Input()
|
||||
editable: boolean;
|
||||
|
||||
@Input()
|
||||
displayEmpty: boolean = true;
|
||||
|
||||
@ViewChild(CardViewContentProxyDirective)
|
||||
private content: CardViewContentProxyDirective;
|
||||
|
||||
@@ -63,13 +68,19 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(...args) {
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (!this.loaded) {
|
||||
this.loadComponent();
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
this.proxy('ngOnChanges', ...args);
|
||||
Object.keys(changes)
|
||||
.map(changeName => [changeName, changes[changeName]])
|
||||
.forEach(([inputParamName, simpleChange]: [string, SimpleChange]) => {
|
||||
this.componentReference.instance[inputParamName] = simpleChange.currentValue;
|
||||
});
|
||||
|
||||
this.proxy('ngOnChanges', changes);
|
||||
}
|
||||
|
||||
private loadComponent() {
|
||||
@@ -80,6 +91,7 @@ export class CardViewItemDispatcherComponent implements OnChanges {
|
||||
|
||||
this.componentReference.instance.editable = this.editable;
|
||||
this.componentReference.instance.property = this.property;
|
||||
this.componentReference.instance.displayEmpty = this.displayEmpty;
|
||||
}
|
||||
|
||||
private proxy(methodName, ...args) {
|
@@ -1,12 +1,12 @@
|
||||
<div class="adf-property-label">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-label" *ngIf="showProperty()">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-value">
|
||||
<div>
|
||||
<span *ngIf="!isClickable(); else elseBlock" [attr.data-automation-id]="'card-mapitem-value-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
<span *ngIf="showProperty();">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<ng-template #elseBlock>
|
||||
<span class="adf-mapitem-clickable-value" (click)="clicked()" [attr.data-automation-id]="'card-mapitem-value-' + property.key">
|
||||
<span *ngIf="!property.isEmpty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
</ng-template>
|
||||
</div>
|
@@ -19,15 +19,15 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { MaterialModule } from '../../../material.module';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { CardViewMapItemModel } from '../models/card-view-mapitem.model';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { LogService } from '../services/log.service';
|
||||
import { TranslateLoaderService } from '../services/translate-loader.service';
|
||||
import { CardViewMapItemModel } from '../../models/card-view-mapitem.model';
|
||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
import { LogService } from '../../../services/log.service';
|
||||
import { TranslateLoaderService } from '../../../services/translate-loader.service';
|
||||
|
||||
import { CardViewMapItemComponent } from './card-view-mapitem.component';
|
||||
|
||||
@@ -77,14 +77,15 @@ describe('CardViewMapItemComponent', () => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should render the default if the value is empty', () => {
|
||||
it('should render the default if the value is empty and displayEmpty is true', () => {
|
||||
component.property = new CardViewMapItemModel({
|
||||
label: 'Map label',
|
||||
value: null,
|
||||
key: 'mapkey',
|
||||
default: 'Fake default'
|
||||
default: 'Fake default',
|
||||
clickable: false
|
||||
});
|
||||
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let labelValue = debug.query(By.css('.adf-property-label'));
|
||||
@@ -96,6 +97,25 @@ describe('CardViewMapItemComponent', () => {
|
||||
expect(value.nativeElement.innerText.trim()).toBe('Fake default');
|
||||
});
|
||||
|
||||
it('should NOT render the default if the value is empty and displayEmpty is false', () => {
|
||||
component.property = new CardViewMapItemModel({
|
||||
label: 'Map label',
|
||||
value: null,
|
||||
key: 'mapkey',
|
||||
default: 'Fake default',
|
||||
clickable: false
|
||||
});
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let labelValue = debug.query(By.css('.adf-property-label'));
|
||||
expect(labelValue).toBeNull();
|
||||
|
||||
let value = debug.query(By.css(`[data-automation-id="card-mapitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('');
|
||||
});
|
||||
|
||||
it('should render the label and value', () => {
|
||||
component.property = new CardViewMapItemModel({
|
||||
label: 'Map label',
|
@@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CardViewMapItemModel } from '../models/card-view-mapitem.model';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { CardViewMapItemModel } from '../../models/card-view-mapitem.model';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-card-view-mapitem',
|
||||
@@ -29,8 +29,15 @@ export class CardViewMapItemComponent {
|
||||
@Input()
|
||||
property: CardViewMapItemModel;
|
||||
|
||||
@Input()
|
||||
displayEmpty: boolean = true;
|
||||
|
||||
constructor(private cardViewUpdateService: CardViewUpdateService) {}
|
||||
|
||||
showProperty() {
|
||||
return this.displayEmpty || !this.property.isEmpty();
|
||||
}
|
||||
|
||||
isClickable() {
|
||||
return this.property.clickable;
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
<div class="adf-property-label" *ngIf="showProperty() || isEditable()">{{ property.label | translate }}</div>
|
||||
<div class="adf-property-value">
|
||||
<span *ngIf="!isEditable()">
|
||||
<span *ngIf="!isClickable(); else elseBlock" [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="showProperty()">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<ng-template #elseBlock>
|
||||
<span class="adf-textitem-clickable-value" (click)="clicked()" [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
</ng-template>
|
||||
</span>
|
||||
<span *ngIf="isEditable()">
|
||||
<div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<span [attr.data-automation-id]="'card-textitem-value-' + property.key">
|
||||
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
|
||||
</span>
|
||||
<mat-icon fxFlex="0 0 auto" [attr.data-automation-id]="'card-textitem-edit-icon-' + property.key" class="adf-textitem-icon">create</mat-icon>
|
||||
</div>
|
||||
<div *ngIf="inEdit" class="adf-textitem-editable">
|
||||
<div class="adf-textitem-editable-controls">
|
||||
<mat-form-field floatPlaceholder="never" class="adf-input-container">
|
||||
<input *ngIf="!property.multiline" #editorInput
|
||||
matInput
|
||||
class="adf-input"
|
||||
[placeholder]="property.default | translate"
|
||||
[(ngModel)]="editedValue"
|
||||
[attr.data-automation-id]="'card-textitem-editinput-' + property.key">
|
||||
<textarea *ngIf="property.multiline" #editorInput
|
||||
matInput
|
||||
matTextareaAutosize
|
||||
matAutosizeMaxRows="1"
|
||||
matAutosizeMaxRows="5"
|
||||
class="adf-textarea"
|
||||
[placeholder]="property.default | translate"
|
||||
[(ngModel)]="editedValue"
|
||||
[attr.data-automation-id]="'card-textitem-edittextarea-' + property.key"></textarea>
|
||||
</mat-form-field>
|
||||
<mat-icon
|
||||
class="adf-textitem-icon adf-update-icon"
|
||||
(click)="update()"
|
||||
[attr.data-automation-id]="'card-textitem-update-' + property.key">done</mat-icon>
|
||||
<mat-icon
|
||||
class="adf-textitem-icon adf-reset-icon"
|
||||
(click)="reset()"
|
||||
[attr.data-automation-id]="'card-textitem-reset-' + property.key">clear</mat-icon>
|
||||
</div>
|
||||
<mat-error class="adf-textitem-editable-error" *ngIf="hasErrors()">
|
||||
<ul>
|
||||
<li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li>
|
||||
</ul>
|
||||
</mat-error>
|
||||
</div>
|
||||
</span>
|
||||
<ng-template #elseEmptyValueBlock>
|
||||
<span class="adf-textitem-default-value">{{ property.default | translate }}</span>
|
||||
</ng-template>
|
||||
</div>
|
@@ -30,23 +30,46 @@
|
||||
}
|
||||
|
||||
&-textitem-editable {
|
||||
display: flex;
|
||||
|
||||
mat-icon:hover {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
&-controls {
|
||||
display: flex;
|
||||
|
||||
mat-icon:hover {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
border: 1px solid mat-color($foreground, text, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
&-error {
|
||||
font-size: 12px;
|
||||
padding-top: 4px;
|
||||
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
border: 1px solid mat-color($foreground, text, 0.15);
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-textitem-default-value {
|
||||
color: mat-color($foreground, text, 0.54);
|
||||
}
|
||||
|
||||
&-textitem-editable .mat-input-wrapper {
|
||||
margin: 0;
|
||||
padding-bottom: 0;
|
||||
@@ -67,7 +90,7 @@
|
||||
}
|
||||
|
||||
&-textitem-editable .mat-input-placeholder {
|
||||
top: 0;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
&-textitem-editable .mat-input-element {
|
@@ -0,0 +1,325 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatDatepickerModule, MatIconModule, MatInputModule, MatNativeDateModule } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
import { LogService } from '../../../services/log.service';
|
||||
import { TranslateLoaderService } from '../../../services/translate-loader.service';
|
||||
|
||||
import { CardViewTextItemComponent } from './card-view-textitem.component';
|
||||
|
||||
describe('CardViewTextItemComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<CardViewTextItemComponent>;
|
||||
let component: CardViewTextItemComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NoopAnimationsModule,
|
||||
MatDatepickerModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatNativeDateModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderService
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
CardViewTextItemComponent
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
CardViewUpdateService,
|
||||
LogService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CardViewTextItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: 'Lorem ipsum',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: false
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
||||
it('should render the label and value', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let labelValue = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(labelValue).not.toBeNull();
|
||||
expect(labelValue.nativeElement.innerText).toBe('Text label');
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('Lorem ipsum');
|
||||
});
|
||||
|
||||
it('should NOT render the default as value if the value is empty, editable is false and displayEmpty is false', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: false
|
||||
});
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty, editable is false and displayEmpty is true', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: false
|
||||
});
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and editable true', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
editable: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should NOT render the default as value if the value is empty, clickable is false and displayEmpty is false', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
clickable: false
|
||||
});
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty, clickable is false and displayEmpty is true', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
clickable: false
|
||||
});
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render the default as value if the value is empty and clickable true', () => {
|
||||
component.property = new CardViewTextItemModel ({
|
||||
label: 'Text label',
|
||||
value: '',
|
||||
key: 'textkey',
|
||||
default: 'FAKE-DEFAULT-KEY',
|
||||
clickable: true
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('FAKE-DEFAULT-KEY');
|
||||
});
|
||||
|
||||
it('should render value when editable:true', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-value-${component.property.key}"]`));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('Lorem ipsum');
|
||||
});
|
||||
|
||||
it('should render the edit icon in case of editable:true', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-${component.property.key}"]`));
|
||||
expect(editIcon).not.toBeNull('Edit icon should be shown');
|
||||
});
|
||||
|
||||
it('should NOT render the edit icon in case of editable:false', () => {
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-${component.property.key}"]`));
|
||||
expect(editIcon).toBeNull('Edit icon should NOT be shown');
|
||||
});
|
||||
|
||||
it('should NOT render the picker and toggle in case of editable:true but (general) editable:false', () => {
|
||||
component.editable = false;
|
||||
component.property.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-${component.property.key}"]`));
|
||||
expect(editIcon).toBeNull('Edit icon should NOT be shown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.inEdit = true;
|
||||
component.editedValue = 'updated-value';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should call the isValid method with the edited value', () => {
|
||||
spyOn(component.property, 'isValid');
|
||||
component.editedValue = 'updated-value';
|
||||
|
||||
component.update();
|
||||
|
||||
expect(component.property.isValid).toHaveBeenCalledWith('updated-value');
|
||||
});
|
||||
|
||||
it('should trigger the update event if the editedValue is valid', () => {
|
||||
component.property.isValid = () => true;
|
||||
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
spyOn(cardViewUpdateService, 'update');
|
||||
component.editedValue = 'updated-value';
|
||||
|
||||
component.update();
|
||||
|
||||
expect(cardViewUpdateService.update).toHaveBeenCalledWith(component.property, 'updated-value');
|
||||
});
|
||||
|
||||
it('should NOT trigger the update event if the editedValue is invalid', () => {
|
||||
component.property.isValid = () => false;
|
||||
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
spyOn(cardViewUpdateService, 'update');
|
||||
|
||||
component.update();
|
||||
|
||||
expect(cardViewUpdateService.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set the errorMessages properly if the editedValue is invalid', () => {
|
||||
const expectedErrorMessages = ['Something went wrong'];
|
||||
component.property.isValid = () => false;
|
||||
component.property.getValidationErrors = () => expectedErrorMessages;
|
||||
|
||||
component.update();
|
||||
|
||||
expect(component.errorMessages).toBe(expectedErrorMessages);
|
||||
});
|
||||
|
||||
it('should update the propery\'s value after a succesful update attempt', async(() => {
|
||||
component.property.isValid = () => true;
|
||||
component.update();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.property.value).toBe(component.editedValue);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should switch back to readonly mode after an update attempt', async(() => {
|
||||
component.property.isValid = () => true;
|
||||
component.update();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.inEdit).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should trigger an update event on the CardViewUpdateService [integration]', (done) => {
|
||||
component.inEdit = false;
|
||||
component.property.isValid = () => true;
|
||||
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
|
||||
const expectedText = 'changed text';
|
||||
fixture.detectChanges();
|
||||
|
||||
cardViewUpdateService.itemUpdated$.subscribe(
|
||||
(updateNotification) => {
|
||||
expect(updateNotification.target).toBe(component.property);
|
||||
expect(updateNotification.changed).toEqual({ textkey: expectedText });
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
let editIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-toggle-${component.property.key}"]`));
|
||||
editIcon.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
let editInput = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-editinput-${component.property.key}"]`));
|
||||
editInput.nativeElement.value = expectedText;
|
||||
editInput.nativeElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
|
||||
let updateInput = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-update-${component.property.key}"]`));
|
||||
updateInput.triggerEventHandler('click', null);
|
||||
});
|
||||
});
|
||||
});
|
@@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
import { Component, Input, OnChanges, ViewChild } from '@angular/core';
|
||||
import { CardViewTextItemModel } from '../models/card-view-textitem.model';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-card-view-textitem',
|
||||
@@ -29,28 +29,40 @@ export class CardViewTextItemComponent implements OnChanges {
|
||||
property: CardViewTextItemModel;
|
||||
|
||||
@Input()
|
||||
editable: boolean;
|
||||
editable: boolean = false;
|
||||
|
||||
@Input()
|
||||
displayEmpty: boolean = true;
|
||||
|
||||
@ViewChild('editorInput')
|
||||
private editorInput: any;
|
||||
|
||||
inEdit: boolean = false;
|
||||
editedValue: string;
|
||||
errorMessages: string[];
|
||||
|
||||
constructor(private cardViewUpdateService: CardViewUpdateService) {}
|
||||
|
||||
ngOnChanges() {
|
||||
ngOnChanges(): void {
|
||||
this.editedValue = this.property.value;
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
showProperty(): boolean {
|
||||
return this.displayEmpty || !this.property.isEmpty();
|
||||
}
|
||||
|
||||
isEditable(): boolean {
|
||||
return this.editable && this.property.editable;
|
||||
}
|
||||
|
||||
isClickable() {
|
||||
isClickable(): boolean {
|
||||
return this.property.clickable;
|
||||
}
|
||||
|
||||
hasErrors(): number {
|
||||
return this.errorMessages && this.errorMessages.length;
|
||||
}
|
||||
|
||||
setEditMode(editStatus: boolean): void {
|
||||
this.inEdit = editStatus;
|
||||
setTimeout(() => {
|
||||
@@ -66,9 +78,13 @@ export class CardViewTextItemComponent implements OnChanges {
|
||||
}
|
||||
|
||||
update(): void {
|
||||
this.cardViewUpdateService.update(this.property, this.editedValue );
|
||||
this.property.value = this.editedValue;
|
||||
this.setEditMode(false);
|
||||
if (this.property.isValid(this.editedValue)) {
|
||||
this.cardViewUpdateService.update(this.property, this.editedValue );
|
||||
this.property.value = this.editedValue;
|
||||
this.setEditMode(false);
|
||||
} else {
|
||||
this.errorMessages = this.property.getValidationErrors(this.editedValue);
|
||||
}
|
||||
}
|
||||
|
||||
clicked(): void {
|
23
lib/core/card-view/components/card-view.components.ts
Normal file
23
lib/core/card-view/components/card-view.components.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export * from './card-view/card-view.component';
|
||||
export * from './card-view-boolitem/card-view-boolitem.component';
|
||||
export * from './card-view-dateitem/card-view-dateitem.component';
|
||||
export * from './card-view-item-dispatcher/card-view-item-dispatcher.component';
|
||||
export * from './card-view-mapitem/card-view-mapitem.component';
|
||||
export * from './card-view-textitem/card-view-textitem.component';
|
@@ -0,0 +1,11 @@
|
||||
<div class="adf-property-list">
|
||||
<div *ngFor="let property of properties">
|
||||
<div [attr.data-automation-id]="'header-'+property.key" class="adf-property">
|
||||
<adf-card-view-item-dispatcher
|
||||
[property]="property"
|
||||
[editable]="editable"
|
||||
[displayEmpty]="displayEmpty">
|
||||
</adf-card-view-item-dispatcher>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,22 @@
|
||||
|
||||
@mixin adf-card-view-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf-property-list {
|
||||
.adf-property {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.adf-property-label {
|
||||
font-size: 12px;
|
||||
color: mat-color($foreground, text, 0.4);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.adf-property-value {
|
||||
font-size: 14px;
|
||||
color: mat-color($foreground, text, 0.87);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,24 +20,26 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatDatepickerModule, MatIconModule, MatInputModule, MatNativeDateModule } from '@angular/material';
|
||||
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||
|
||||
import { CardViewDateItemModel } from '../models/card-view-dateitem.model';
|
||||
import { CardViewTextItemModel } from '../models/card-view-textitem.model';
|
||||
import { CardViewUpdateService } from '../services/card-view-update.service';
|
||||
import { CardViewDateItemModel } from '../../models/card-view-dateitem.model';
|
||||
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
|
||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||
|
||||
import { TranslateLoaderService } from '../services/translate-loader.service';
|
||||
import { CardViewContentProxyDirective } from './card-view-content-proxy.directive';
|
||||
import { CardViewDateItemComponent } from './card-view-dateitem.component';
|
||||
import { CardViewItemDispatcherComponent } from './card-view-item-dispatcher.component';
|
||||
import { CardViewTextItemComponent } from './card-view-textitem.component';
|
||||
import { TranslateLoaderService } from '../../../services/translate-loader.service';
|
||||
import { CardViewContentProxyDirective } from '../../directives/card-view-content-proxy.directive';
|
||||
import { CardViewDateItemComponent } from '../card-view-dateitem/card-view-dateitem.component';
|
||||
import { CardItemTypeService } from '../../services/card-item-types.service';
|
||||
import { CardViewItemDispatcherComponent } from '../card-view-item-dispatcher/card-view-item-dispatcher.component';
|
||||
import { CardViewTextItemComponent } from '../card-view-textitem/card-view-textitem.component';
|
||||
import { CardViewComponent } from './card-view.component';
|
||||
|
||||
describe('AdfCardView', () => {
|
||||
describe('CardViewComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<CardViewComponent>;
|
||||
let component: CardViewComponent;
|
||||
@@ -50,6 +52,8 @@ describe('AdfCardView', () => {
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatNativeDateModule,
|
||||
MatDatetimepickerModule,
|
||||
MatNativeDatetimeModule,
|
||||
FormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
@@ -66,6 +70,7 @@ describe('AdfCardView', () => {
|
||||
CardViewDateItemComponent
|
||||
],
|
||||
providers: [
|
||||
CardItemTypeService,
|
||||
CardViewUpdateService,
|
||||
AppConfigService
|
||||
]
|
||||
@@ -135,14 +140,42 @@ describe('AdfCardView', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should render the default value if the value is empty', async(() => {
|
||||
it('should NOT render anything if the value is empty, not editable and displayEmpty is false', async(() => {
|
||||
component.properties = [new CardViewTextItemModel({
|
||||
label: 'My default label',
|
||||
value: null,
|
||||
default: 'default value',
|
||||
key: 'some key'
|
||||
key: 'some-key',
|
||||
editable: false
|
||||
})];
|
||||
component.editable = true;
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let labelValue = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(labelValue).toBeNull();
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should render the default value if the value is empty, not editable and displayEmpty is true', async(() => {
|
||||
component.properties = [new CardViewTextItemModel({
|
||||
label: 'My default label',
|
||||
value: null,
|
||||
default: 'default value',
|
||||
key: 'some-key',
|
||||
editable: false
|
||||
})];
|
||||
component.editable = true;
|
||||
component.displayEmpty = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -150,9 +183,34 @@ describe('AdfCardView', () => {
|
||||
expect(labelValue).not.toBeNull();
|
||||
expect(labelValue.nativeElement.innerText).toBe('My default label');
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value'));
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value [data-automation-id="card-textitem-value-some-key"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText).toBe('default value');
|
||||
expect(value.nativeElement.innerText.trim()).toBe('default value');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should render the default value if the value is empty and is editable', async(() => {
|
||||
component.properties = [new CardViewTextItemModel({
|
||||
label: 'My default label',
|
||||
value: null,
|
||||
default: 'default value',
|
||||
key: 'some-key',
|
||||
editable: true
|
||||
})];
|
||||
component.editable = true;
|
||||
component.displayEmpty = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let labelValue = fixture.debugElement.query(By.css('.adf-property-label'));
|
||||
expect(labelValue).not.toBeNull();
|
||||
expect(labelValue.nativeElement.innerText).toBe('My default label');
|
||||
|
||||
let value = fixture.debugElement.query(By.css('.adf-property-value [data-automation-id="card-textitem-value-some-key"]'));
|
||||
expect(value).not.toBeNull();
|
||||
expect(value.nativeElement.innerText.trim()).toBe('default value');
|
||||
});
|
||||
}));
|
||||
});
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { CardViewItem } from '../../interfaces/card-view-item.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-card-view',
|
||||
@@ -29,4 +29,7 @@ export class CardViewComponent {
|
||||
|
||||
@Input()
|
||||
editable: boolean;
|
||||
|
||||
@Input()
|
||||
displayEmpty: boolean = true;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemProperties } from './card-view-item-properties.interface';
|
||||
|
||||
export interface CardViewBoolItemProperties extends CardViewItemProperties {
|
||||
value: any;
|
||||
default?: boolean;
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemProperties } from './card-view-item-properties.interface';
|
||||
|
||||
export interface CardViewDateItemProperties extends CardViewItemProperties {
|
||||
format?: string;
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemValidator } from './card-view-item-validator.interface';
|
||||
|
||||
export interface CardViewItemProperties {
|
||||
label: string;
|
||||
value: any;
|
||||
key: any;
|
||||
default?: any;
|
||||
editable?: boolean;
|
||||
clickable?: boolean;
|
||||
validators?: CardViewItemValidator[];
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export interface CardViewItemValidator {
|
||||
message: string;
|
||||
isValid(value: any): boolean;
|
||||
}
|
@@ -21,6 +21,6 @@ export interface CardViewItem {
|
||||
key: string;
|
||||
default?: any;
|
||||
type: string;
|
||||
displayValue: string;
|
||||
displayValue: any;
|
||||
editable?: boolean;
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { PipeTransform } from '@angular/core';
|
||||
|
||||
export interface CardViewTextItemPipeProperty {
|
||||
pipe: PipeTransform;
|
||||
params?: any[];
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemProperties } from './card-view-item-properties.interface';
|
||||
import { CardViewTextItemPipeProperty } from './card-view-textitem-pipe-property.interface';
|
||||
|
||||
export interface CardViewTextItemProperties extends CardViewItemProperties {
|
||||
multiline?: boolean;
|
||||
pipes?: CardViewTextItemPipeProperty[];
|
||||
}
|
24
lib/core/card-view/interfaces/card-view.interfaces.ts
Normal file
24
lib/core/card-view/interfaces/card-view.interfaces.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export * from './card-view-item-properties.interface';
|
||||
export * from './card-view-item-validator.interface';
|
||||
export * from './card-view-item.interface';
|
||||
export * from './card-view-textitem-properties.interface';
|
||||
export * from './card-view-dateitem-properties.interface';
|
||||
export * from './card-view-boolitem-properties.interface';
|
||||
export * from './card-view-textitem-pipe-property.interface';
|
85
lib/core/card-view/models/card-view-baseitem.model.spec.ts
Normal file
85
lib/core/card-view/models/card-view-baseitem.model.spec.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemProperties } from '../interfaces/card-view.interfaces';
|
||||
import { CardViewBaseItemModel } from './card-view-baseitem.model';
|
||||
import { CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
|
||||
class CarViewCustomItemModel extends CardViewBaseItemModel {}
|
||||
|
||||
describe('CardViewBaseItemModel', () => {
|
||||
|
||||
let properties: CardViewItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Tribe',
|
||||
value: 'Oseram',
|
||||
key: 'tribe'
|
||||
};
|
||||
});
|
||||
|
||||
describe('isValid & Validation errors', () => {
|
||||
|
||||
it('should be true when no validators are set', () => {
|
||||
const itemModel = new CarViewCustomItemModel(properties);
|
||||
|
||||
const isValid = itemModel.isValid(null);
|
||||
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
|
||||
it('should call the registered validators to validate', () => {
|
||||
const validator1: CardViewItemValidator = { isValid: () => true, message: 'validator 1' };
|
||||
const validator2: CardViewItemValidator = { isValid: () => true, message: 'validator 2' };
|
||||
spyOn(validator1, 'isValid');
|
||||
spyOn(validator2, 'isValid');
|
||||
properties.validators = [ validator1, validator2 ];
|
||||
const itemModel = new CarViewCustomItemModel(properties);
|
||||
|
||||
itemModel.isValid('test-against-this');
|
||||
|
||||
expect(validator1.isValid).toHaveBeenCalledWith('test-against-this');
|
||||
expect(validator2.isValid).toHaveBeenCalledWith('test-against-this');
|
||||
});
|
||||
|
||||
it('should return the registered validators\' common decision (case true)', () => {
|
||||
const validator1: CardViewItemValidator = { isValid: () => true, message: 'validator 1' };
|
||||
const validator2: CardViewItemValidator = { isValid: () => true, message: 'validator 2' };
|
||||
properties.validators = [ validator1, validator2 ];
|
||||
const itemModel = new CarViewCustomItemModel(properties);
|
||||
|
||||
const isValid = itemModel.isValid('test-against-this');
|
||||
|
||||
expect(isValid).toBe(true);
|
||||
expect(itemModel.getValidationErrors('test-against-this')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return the registered validators\' common decision (case false)', () => {
|
||||
const validator1: CardViewItemValidator = { isValid: () => false, message: 'validator 1' };
|
||||
const validator2: CardViewItemValidator = { isValid: () => true, message: 'validator 2' };
|
||||
const validator3: CardViewItemValidator = { isValid: () => false, message: 'validator 3' };
|
||||
properties.validators = [ validator1, validator2, validator3 ];
|
||||
const itemModel = new CarViewCustomItemModel(properties);
|
||||
|
||||
const isValid = itemModel.isValid('test-against-this');
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
expect(itemModel.getValidationErrors('test-against-this')).toEqual(['validator 1', 'validator 3']);
|
||||
});
|
||||
});
|
||||
});
|
@@ -15,30 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* This object represent the basic structure of a card view.
|
||||
*
|
||||
*
|
||||
* @returns {CardViewBaseItemModel} .
|
||||
*/
|
||||
|
||||
export interface CardViewItemProperties {
|
||||
label: string;
|
||||
value: any;
|
||||
key: any;
|
||||
default?: string;
|
||||
editable?: boolean;
|
||||
clickable?: boolean;
|
||||
}
|
||||
import { CardViewItemProperties, CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export abstract class CardViewBaseItemModel {
|
||||
label: string;
|
||||
value: any;
|
||||
key: any;
|
||||
default: string;
|
||||
default: any;
|
||||
editable: boolean;
|
||||
clickable: boolean;
|
||||
validators?: CardViewItemValidator[];
|
||||
|
||||
constructor(obj: CardViewItemProperties) {
|
||||
this.label = obj.label || '';
|
||||
@@ -47,9 +33,30 @@ export abstract class CardViewBaseItemModel {
|
||||
this.default = obj.default;
|
||||
this.editable = !!obj.editable;
|
||||
this.clickable = !!obj.clickable;
|
||||
this.validators = obj.validators || [];
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this.value === undefined || this.value === null || this.value === '';
|
||||
}
|
||||
|
||||
isValid(newValue: any): boolean {
|
||||
if (!this.validators.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.validators
|
||||
.map((validator) => validator.isValid(newValue))
|
||||
.reduce((isValidUntilNow, isValid) => isValidUntilNow && isValid, true);
|
||||
}
|
||||
|
||||
getValidationErrors(value): string[] {
|
||||
if (!this.validators.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.validators
|
||||
.filter((validator) => !validator.isValid(value))
|
||||
.map((validator) => validator.message);
|
||||
}
|
||||
}
|
88
lib/core/card-view/models/card-view-boolitem.model.spec.ts
Normal file
88
lib/core/card-view/models/card-view-boolitem.model.spec.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewBoolItemModel } from './card-view-boolitem.model';
|
||||
import { CardViewBoolItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
describe('CardViewFloatItemModel', () => {
|
||||
|
||||
let properties: CardViewBoolItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Tribe',
|
||||
value: undefined,
|
||||
key: 'tribe'
|
||||
};
|
||||
});
|
||||
|
||||
it('true should be parsed as true', () => {
|
||||
properties.value = true;
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(true);
|
||||
});
|
||||
|
||||
it('"true" should be parsed as true', () => {
|
||||
properties.value = 'true';
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(true);
|
||||
});
|
||||
|
||||
it('1 should be parsed as true', () => {
|
||||
properties.value = 1;
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(true);
|
||||
});
|
||||
|
||||
it('"1" should be parsed as true', () => {
|
||||
properties.value = '1';
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(true);
|
||||
});
|
||||
|
||||
it('"false" should be parsed as false', () => {
|
||||
properties.value = 'false';
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(false);
|
||||
});
|
||||
|
||||
it('false should be parsed as false', () => {
|
||||
properties.value = 'false';
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(false);
|
||||
});
|
||||
|
||||
it('undefined should be parsed as false', () => {
|
||||
properties.value = undefined;
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(false);
|
||||
});
|
||||
|
||||
it('null should be parsed as false', () => {
|
||||
properties.value = null;
|
||||
const itemModel = new CardViewBoolItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(false);
|
||||
});
|
||||
});
|
43
lib/core/card-view/models/card-view-boolitem.model.ts
Normal file
43
lib/core/card-view/models/card-view-boolitem.model.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel } from './card-view-baseitem.model';
|
||||
import { CardViewBoolItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export class CardViewBoolItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'bool';
|
||||
value: boolean = false;
|
||||
default: boolean;
|
||||
|
||||
constructor(obj: CardViewBoolItemProperties) {
|
||||
super(obj);
|
||||
|
||||
if (obj.value !== undefined) {
|
||||
this.value = !!JSON.parse(obj.value);
|
||||
}
|
||||
}
|
||||
|
||||
get displayValue() {
|
||||
if (this.isEmpty()) {
|
||||
return this.default;
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,30 +15,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* This object represent the basic structure of a card view.
|
||||
*
|
||||
*
|
||||
* @returns {CardViewDateItemModel} .
|
||||
*/
|
||||
|
||||
import moment from 'moment-es6';
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel, CardViewItemProperties } from './card-view-baseitem.model';
|
||||
|
||||
export interface CardViewDateItemProperties extends CardViewItemProperties {
|
||||
format?: string;
|
||||
}
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel } from './card-view-baseitem.model';
|
||||
import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export class CardViewDateItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'date';
|
||||
format: string;
|
||||
format: string = 'MMM DD YYYY';
|
||||
|
||||
constructor(obj: CardViewDateItemProperties) {
|
||||
super(obj);
|
||||
this.format = obj.format || 'MMM DD YYYY';
|
||||
|
||||
if (obj.format) {
|
||||
this.format = obj.format;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get displayValue() {
|
25
lib/core/card-view/models/card-view-datetimeitem.model.ts
Normal file
25
lib/core/card-view/models/card-view-datetimeitem.model.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewDateItemModel } from './card-view-dateitem.model';
|
||||
|
||||
export class CardViewDatetimeItemModel extends CardViewDateItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'datetime';
|
||||
format: string = 'MMM DD YYYY HH:mm';
|
||||
}
|
59
lib/core/card-view/models/card-view-floatitem.model.spec.ts
Normal file
59
lib/core/card-view/models/card-view-floatitem.model.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewFloatItemModel } from './card-view-floatitem.model';
|
||||
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
describe('CardViewFloatItemModel', () => {
|
||||
|
||||
let properties: CardViewTextItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Tribe',
|
||||
value: '42.42',
|
||||
key: 'tribe'
|
||||
};
|
||||
});
|
||||
|
||||
it('value should be parsed as float', () => {
|
||||
const itemModel = new CardViewFloatItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(42.42);
|
||||
});
|
||||
|
||||
it('value should be parsed as float only if there is a value', () => {
|
||||
properties.value = undefined;
|
||||
const itemModel = new CardViewFloatItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(undefined);
|
||||
});
|
||||
|
||||
it('isValid should return the validator\'s value', () => {
|
||||
const itemModel = new CardViewFloatItemModel(properties);
|
||||
|
||||
expect(itemModel.isValid(42)).toBe(true, 'For 42 it should be true');
|
||||
expect(itemModel.isValid(42.0)).toBe(true, 'For 42.0 it should be true');
|
||||
expect(itemModel.isValid('42')).toBe(true, 'For "42" it should be true');
|
||||
expect(itemModel.isValid('42.0')).toBe(true, 'For "42.0" it should be true');
|
||||
expect(itemModel.isValid('4e2')).toBe(true, 'For "4e2" it should be true');
|
||||
expect(itemModel.isValid('4g2')).toBe(false, 'For "4g2" it should be false');
|
||||
expect(itemModel.isValid(42.3)).toBe(true, 'For 42.3 it should be true');
|
||||
expect(itemModel.isValid('42.3')).toBe(true, 'For "42.3" it should be true');
|
||||
expect(itemModel.isValid('test')).toBe(false, 'For "test" it should be false');
|
||||
});
|
||||
});
|
35
lib/core/card-view/models/card-view-floatitem.model.ts
Normal file
35
lib/core/card-view/models/card-view-floatitem.model.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewTextItemModel } from './card-view-textitem.model';
|
||||
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
|
||||
import { CardViewItemFloatValidator } from '..//validators/card-view.validators';
|
||||
|
||||
export class CardViewFloatItemModel extends CardViewTextItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'float';
|
||||
|
||||
constructor(obj: CardViewTextItemProperties) {
|
||||
super(obj);
|
||||
|
||||
this.validators.push(new CardViewItemFloatValidator());
|
||||
if (obj.value) {
|
||||
this.value = parseFloat(obj.value);
|
||||
}
|
||||
}
|
||||
}
|
59
lib/core/card-view/models/card-view-intitem.model.spec.ts
Normal file
59
lib/core/card-view/models/card-view-intitem.model.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewIntItemModel } from './card-view-intitem.model';
|
||||
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
describe('CardViewIntItemModel', () => {
|
||||
|
||||
let properties: CardViewTextItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Tribe',
|
||||
value: '42',
|
||||
key: 'tribe'
|
||||
};
|
||||
});
|
||||
|
||||
it('value should be parsed as integer', () => {
|
||||
const itemModel = new CardViewIntItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(42);
|
||||
});
|
||||
|
||||
it('value should be parsed as integer only if there is a value', () => {
|
||||
properties.value = undefined;
|
||||
const itemModel = new CardViewIntItemModel(properties);
|
||||
|
||||
expect(itemModel.value).toBe(undefined);
|
||||
});
|
||||
|
||||
it('isValid should return the validator\'s value', () => {
|
||||
const itemModel = new CardViewIntItemModel(properties);
|
||||
|
||||
expect(itemModel.isValid(42)).toBe(true, 'For 42 it should be true');
|
||||
expect(itemModel.isValid(42.0)).toBe(true, 'For 42.0 it should be true');
|
||||
expect(itemModel.isValid('42')).toBe(true, 'For "42" it should be true');
|
||||
expect(itemModel.isValid('42.0')).toBe(true, 'For "42.0" it should be true');
|
||||
expect(itemModel.isValid('4e2')).toBe(true, 'For "4e2" it should be true');
|
||||
expect(itemModel.isValid('4g2')).toBe(false, 'For "4g2" it should be false');
|
||||
expect(itemModel.isValid(42.3)).toBe(false, 'For 42.3 it should be false');
|
||||
expect(itemModel.isValid('42.3')).toBe(false, 'For "42.3" it should be false');
|
||||
expect(itemModel.isValid('test')).toBe(false, 'For "test" it should be false');
|
||||
});
|
||||
});
|
35
lib/core/card-view/models/card-view-intitem.model.ts
Normal file
35
lib/core/card-view/models/card-view-intitem.model.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewTextItemModel } from './card-view-textitem.model';
|
||||
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
|
||||
import { CardViewItemIntValidator } from '../validators/card-view.validators';
|
||||
|
||||
export class CardViewIntItemModel extends CardViewTextItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'int';
|
||||
|
||||
constructor(obj: CardViewTextItemProperties) {
|
||||
super(obj);
|
||||
|
||||
this.validators.push(new CardViewItemIntValidator());
|
||||
if (obj.value) {
|
||||
this.value = parseInt(obj.value, 10);
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,26 +15,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* This object represent the basic structure of a card view.
|
||||
*
|
||||
*
|
||||
* @returns {CardViewMapItemModel} .
|
||||
*/
|
||||
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel, CardViewItemProperties } from './card-view-baseitem.model';
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel } from './card-view-baseitem.model';
|
||||
|
||||
export class CardViewMapItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'map';
|
||||
value: Map<string, string>;
|
||||
|
||||
constructor(obj: CardViewItemProperties) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
get displayValue() {
|
||||
if (this.value && this.value.size > 0) {
|
||||
return this.value.values().next().value;
|
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
|
||||
import { PipeTransform } from '@angular/core';
|
||||
import { CardViewTextItemModel, CardViewTextItemProperties } from './card-view-textitem.model';
|
||||
import { CardViewTextItemModel } from './card-view-textitem.model';
|
||||
import { CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
class TestPipe implements PipeTransform {
|
||||
transform(value: string, pipeParam: string): string {
|
||||
@@ -39,28 +40,36 @@ describe('CardViewTextItemModel', () => {
|
||||
|
||||
describe('displayValue', () => {
|
||||
|
||||
it('should return the extension if file has it', () => {
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
it('should return the value if it is present', () => {
|
||||
const itemModel = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('Banuk');
|
||||
expect(itemModel.displayValue).toBe('Banuk');
|
||||
});
|
||||
|
||||
it('should return the default value if the value is not present', () => {
|
||||
properties.value = undefined;
|
||||
properties.default = 'default-value';
|
||||
const itemModel = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(itemModel.displayValue).toBe('default-value');
|
||||
});
|
||||
|
||||
it('should apply a pipe on the value if it is present', () => {
|
||||
properties.pipes = [
|
||||
{ pipe: new TestPipe() }
|
||||
];
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
const itemModel = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('testpiped-Banuk');
|
||||
expect(itemModel.displayValue).toBe('testpiped-Banuk');
|
||||
});
|
||||
|
||||
it('should apply a pipe on the value with parameters if those are present', () => {
|
||||
properties.pipes = [
|
||||
{ pipe: new TestPipe(), params: ['withParams'] }
|
||||
];
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
const itemModel = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('testpiped-Banuk-withParams');
|
||||
expect(itemModel.displayValue).toBe('testpiped-Banuk-withParams');
|
||||
});
|
||||
|
||||
it('should apply more pipes on the value with parameters if those are present', () => {
|
||||
@@ -70,9 +79,9 @@ describe('CardViewTextItemModel', () => {
|
||||
{ pipe, params: ['2'] },
|
||||
{ pipe, params: ['3'] }
|
||||
];
|
||||
const file = new CardViewTextItemModel(properties);
|
||||
const itemModel = new CardViewTextItemModel(properties);
|
||||
|
||||
expect(file.displayValue).toBe('testpiped-testpiped-testpiped-Banuk-1-2-3');
|
||||
expect(itemModel.displayValue).toBe('testpiped-testpiped-testpiped-Banuk-1-2-3');
|
||||
});
|
||||
});
|
||||
});
|
@@ -15,31 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* This object represent the basic structure of a card view.
|
||||
*
|
||||
*
|
||||
* @returns {CardViewTextItemModel} .
|
||||
*/
|
||||
import { CardViewItem } from '../interfaces/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel } from './card-view-baseitem.model';
|
||||
import { CardViewTextItemPipeProperty, CardViewTextItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
import { PipeTransform } from '@angular/core';
|
||||
import { CardViewItem } from '../interface/card-view-item.interface';
|
||||
import { DynamicComponentModel } from '../services/dynamic-component-mapper.service';
|
||||
import { CardViewBaseItemModel, CardViewItemProperties } from './card-view-baseitem.model';
|
||||
|
||||
export interface CardViewTextItemPipeProperty {
|
||||
pipe: PipeTransform;
|
||||
params?: Array<any>;
|
||||
}
|
||||
export interface CardViewTextItemProperties extends CardViewItemProperties {
|
||||
multiline?: boolean;
|
||||
pipes?: Array<CardViewTextItemPipeProperty>;
|
||||
}
|
||||
export class CardViewTextItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type: string = 'text';
|
||||
multiline?: boolean;
|
||||
pipes?: Array<CardViewTextItemPipeProperty>;
|
||||
pipes?: CardViewTextItemPipeProperty[];
|
||||
|
||||
constructor(obj: CardViewTextItemProperties) {
|
||||
super(obj);
|
||||
@@ -48,7 +32,11 @@ export class CardViewTextItemModel extends CardViewBaseItemModel implements Card
|
||||
}
|
||||
|
||||
get displayValue() {
|
||||
return this.applyPipes(this.value);
|
||||
if (this.isEmpty()) {
|
||||
return this.default;
|
||||
} else {
|
||||
return this.applyPipes(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
private applyPipes(displayValue) {
|
25
lib/core/card-view/models/card-view.models.ts
Normal file
25
lib/core/card-view/models/card-view.models.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export * from './card-view-baseitem.model';
|
||||
export * from './card-view-boolitem.model';
|
||||
export * from './card-view-dateitem.model';
|
||||
export * from './card-view-datetimeitem.model';
|
||||
export * from './card-view-floatitem.model';
|
||||
export * from './card-view-intitem.model';
|
||||
export * from './card-view-mapitem.model';
|
||||
export * from './card-view-textitem.model';
|
@@ -15,9 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './card-view-content-proxy.directive';
|
||||
export * from './card-view-dateitem.component';
|
||||
export * from './card-view-item-dispatcher.component';
|
||||
export * from './card-view-mapitem.component';
|
||||
export * from './card-view-textitem.component';
|
||||
export * from './card-view.component';
|
||||
export {
|
||||
CardViewComponent,
|
||||
CardViewBoolItemComponent,
|
||||
CardViewDateItemComponent,
|
||||
CardViewMapItemComponent,
|
||||
CardViewTextItemComponent
|
||||
} from './components/card-view.components';
|
||||
|
||||
export * from './interfaces/card-view.interfaces';
|
||||
export * from './validators/card-view.validators';
|
||||
export * from './models/card-view.models';
|
||||
export * from './services/card-view.services';
|
||||
export * from './directives/card-view-content-proxy.directive';
|
||||
|
@@ -16,10 +16,11 @@
|
||||
*/
|
||||
|
||||
import { Injectable, Type } from '@angular/core';
|
||||
import { CardViewDateItemComponent } from '../card-view/card-view-dateitem.component';
|
||||
import { CardViewMapItemComponent } from '../card-view/card-view-mapitem.component';
|
||||
import { CardViewTextItemComponent } from '../card-view/card-view-textitem.component';
|
||||
import { DynamicComponentMapper, DynamicComponentResolveFunction, DynamicComponentResolver } from '../services/dynamic-component-mapper.service';
|
||||
import { CardViewDateItemComponent } from '../components/card-view-dateitem/card-view-dateitem.component';
|
||||
import { CardViewMapItemComponent } from '../components/card-view-mapitem/card-view-mapitem.component';
|
||||
import { CardViewTextItemComponent } from '../components/card-view-textitem/card-view-textitem.component';
|
||||
import { CardViewBoolItemComponent } from '../components/card-view-boolitem/card-view-boolitem.component';
|
||||
import { DynamicComponentMapper, DynamicComponentResolveFunction, DynamicComponentResolver } from '../../services/dynamic-component-mapper.service';
|
||||
|
||||
@Injectable()
|
||||
export class CardItemTypeService extends DynamicComponentMapper {
|
||||
@@ -28,7 +29,11 @@ export class CardItemTypeService extends DynamicComponentMapper {
|
||||
|
||||
protected types: { [key: string]: DynamicComponentResolveFunction } = {
|
||||
'text': DynamicComponentResolver.fromType(CardViewTextItemComponent),
|
||||
'int': DynamicComponentResolver.fromType(CardViewTextItemComponent),
|
||||
'float': DynamicComponentResolver.fromType(CardViewTextItemComponent),
|
||||
'date': DynamicComponentResolver.fromType(CardViewDateItemComponent),
|
||||
'datetime': DynamicComponentResolver.fromType(CardViewDateItemComponent),
|
||||
'bool': DynamicComponentResolver.fromType(CardViewBoolItemComponent),
|
||||
'map': DynamicComponentResolver.fromType(CardViewMapItemComponent)
|
||||
};
|
||||
}
|
19
lib/core/card-view/services/card-view.services.ts
Normal file
19
lib/core/card-view/services/card-view.services.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export * from './card-item-types.service';
|
||||
export * from './card-view-update.service';
|
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export class CardViewItemFloatValidator implements CardViewItemValidator {
|
||||
|
||||
message = 'CORE.CARDVIEW.VALIDATORS.FLOAT_VALIDATION_ERROR';
|
||||
|
||||
isValid(value: any): boolean {
|
||||
return !isNaN(parseFloat(value)) && isFinite(value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { CardViewItemValidator } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export class CardViewItemIntValidator implements CardViewItemValidator {
|
||||
|
||||
message = 'CORE.CARDVIEW.VALIDATORS.INT_VALIDATION_ERROR';
|
||||
|
||||
isValid(value: any): boolean {
|
||||
return !isNaN(value) && (function(x) { return (x | 0) === x; })(parseFloat(value));
|
||||
}
|
||||
}
|
19
lib/core/card-view/validators/card-view.validators.ts
Normal file
19
lib/core/card-view/validators/card-view.validators.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
export * from './card-view-item-int.validator';
|
||||
export * from './card-view-item-float.validator';
|
@@ -83,8 +83,15 @@
|
||||
"APPLY": "APPLY",
|
||||
"NOT_VALID": "http(s)://host|ip:port(/path) not recognized, try a different URL."
|
||||
},
|
||||
"CARDVIEW": {
|
||||
"VALIDATORS": {
|
||||
"FLOAT_VALIDATION_ERROR": "The value has to be number",
|
||||
"INT_VALIDATION_ERROR": "The value has to be integer"
|
||||
}
|
||||
},
|
||||
"METADATA": {
|
||||
"BASIC": {
|
||||
"HEADER": "Properties",
|
||||
"NAME": "Name",
|
||||
"TITLE": "Title",
|
||||
"DESCRIPTION": "Description",
|
||||
|
@@ -16,5 +16,4 @@
|
||||
*/
|
||||
|
||||
export * from './authentication.interface';
|
||||
export * from './card-view-item.interface';
|
||||
export * from './injection.tokens';
|
||||
|
@@ -15,10 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './card-view-baseitem.model';
|
||||
export * from './card-view-textitem.model';
|
||||
export * from './card-view-mapitem.model';
|
||||
export * from './card-view-dateitem.model';
|
||||
export * from './file.model';
|
||||
export * from './permissions.enum';
|
||||
export * from './product-version.model';
|
||||
|
@@ -19,7 +19,7 @@ import { Injectable } from '@angular/core';
|
||||
import {
|
||||
AlfrescoApi, ContentApi, FavoritesApi, NodesApi,
|
||||
PeopleApi, RenditionsApi, SharedlinksApi, SitesApi,
|
||||
VersionsApi
|
||||
VersionsApi, ClassesApi
|
||||
} from 'alfresco-js-api';
|
||||
import * as alfrescoApi from 'alfresco-js-api';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
@@ -70,6 +70,10 @@ export class AlfrescoApiService {
|
||||
return this.getInstance().core.versionsApi;
|
||||
}
|
||||
|
||||
get classesApi(): ClassesApi {
|
||||
return this.getInstance().core.classesApi;
|
||||
}
|
||||
|
||||
constructor(private appConfig: AppConfigService,
|
||||
private storage: StorageService) {
|
||||
|
||||
|
@@ -34,8 +34,6 @@ export * from './translate-loader.service';
|
||||
export * from './thumbnail.service';
|
||||
export * from './upload.service';
|
||||
export * from './dynamic-component-mapper.service';
|
||||
export * from './card-item-types.service';
|
||||
export * from './card-view-update.service';
|
||||
export * from './user-preferences.service';
|
||||
export * from './highlight-transform.service';
|
||||
export * from './deleted-nodes-api.service';
|
||||
|
@@ -23,8 +23,6 @@ import { AuthGuardBpm } from './auth-guard-bpm.service';
|
||||
import { AuthGuardEcm } from './auth-guard-ecm.service';
|
||||
import { AuthGuard } from './auth-guard.service';
|
||||
import { AuthenticationService } from './authentication.service';
|
||||
import { CardItemTypeService } from './card-item-types.service';
|
||||
import { CardViewUpdateService } from './card-view-update.service';
|
||||
import { CommentProcessService } from './comment-process.service';
|
||||
import { ContentService } from './content.service';
|
||||
import { CookieService } from './cookie.service';
|
||||
@@ -72,8 +70,6 @@ import { UserPreferencesService } from './user-preferences.service';
|
||||
TranslateLoaderService,
|
||||
ThumbnailService,
|
||||
UploadService,
|
||||
CardItemTypeService,
|
||||
CardViewUpdateService,
|
||||
UserPreferencesService,
|
||||
HighlightTransformService,
|
||||
DeletedNodesApiService,
|
||||
|
@@ -2,10 +2,7 @@
|
||||
@import './default-class';
|
||||
@import './theming';
|
||||
|
||||
|
||||
@import '../card-view/card-view-dateitem.component';
|
||||
@import '../card-view/card-view-textitem.component';
|
||||
@import '../card-view/card-view.component';
|
||||
@import '../card-view/card-view.module';
|
||||
@import '../collapsable/accordion-group.component';
|
||||
@import '../datatable/components/datatable/datatable.component';
|
||||
@import '../form/components/widgets/container/container.widget';
|
||||
@@ -25,9 +22,7 @@
|
||||
@mixin adf-core-theme($theme) {
|
||||
@include adf-colors-theme($theme);
|
||||
@include adf-default-class-theme($theme);
|
||||
@include adf-card-view-dateitem-theme($theme);
|
||||
@include adf-card-view-textitem-theme($theme);
|
||||
@include adf-card-view-theme($theme);
|
||||
@include adf-card-view-module-theme($theme);
|
||||
@include adf-accordion-theme($theme);
|
||||
@include adf-datatable-theme($theme);
|
||||
@include adf-form-container-widget-theme($theme);
|
||||
|
@@ -11,6 +11,8 @@
|
||||
"minimatch": "minimatch",
|
||||
"@angular/platform-browser/animations": "@angular/platform-browser/animations",
|
||||
"@angular/material": "@angular/material",
|
||||
"@mat-datetimepicker/core": "@mat-datetimepicker/core",
|
||||
"@mat-datetimepicker/moment": "@mat-datetimepicker/moment",
|
||||
"@angular/flex-layout": "@angular/flex-layout",
|
||||
"@angular/material-moment-adapter": "@angular/material-moment-adapter",
|
||||
"@angular/animations": "@angular/animations",
|
||||
|
@@ -62,6 +62,8 @@
|
||||
"@angular/platform-browser": "5.1.1",
|
||||
"@angular/platform-browser-dynamic": "5.1.1",
|
||||
"@angular/router": "5.1.1",
|
||||
"@mat-datetimepicker/core": "^1.0.1",
|
||||
"@mat-datetimepicker/moment": "^1.0.1",
|
||||
"@ngx-translate/core": "8.0.0",
|
||||
"alfresco-js-api": "2.0.0",
|
||||
"chart.js": "2.5.0",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user