[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:
Popovics András
2018-01-11 12:31:22 +00:00
committed by Eugenio Romano
parent 994041fb23
commit 783f7f0497
106 changed files with 3816 additions and 724 deletions

View File

@@ -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>

View File

@@ -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;
}
}
}
}
}

View File

@@ -0,0 +1,233 @@
/*!
* @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 { 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 { CardViewDateItemComponent } from './card-view-dateitem.component';
describe('CardViewDateItemComponent', () => {
let fixture: ComponentFixture<CardViewDateItemComponent>;
let component: CardViewDateItemComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
HttpClientModule,
MatDatepickerModule,
MatInputModule,
MatNativeDateModule,
MatNativeDatetimeModule,
MatDatetimepickerModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
})
],
declarations: [
CardViewDateItemComponent
],
providers: [
CardViewUpdateService,
AppConfigService
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CardViewDateItemComponent);
component = fixture.componentInstance;
component.property = new CardViewDateItemModel({
label: 'Date label',
value: new Date('07/10/2017'),
key: 'datekey',
default: '',
format: '',
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('Date label');
let value = fixture.debugElement.query(By.css('.adf-property-value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText.trim()).toBe('Jul 10 2017');
});
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: '',
key: 'datekey',
default: 'FAKE-DEFAULT-KEY',
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'));
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 CardViewDateItemModel ({
label: 'Date label',
value: '',
key: 'datekey',
default: 'FAKE-DEFAULT-KEY',
format: '',
editable: true
});
component.editable = true;
fixture.detectChanges();
let value = fixture.debugElement.query(By.css('.adf-property-value'));
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('.adf-property-value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText.trim()).toBe('Jul 10 2017');
});
it('should render the picker and toggle in case of editable:true', () => {
component.editable = true;
component.property.editable = true;
fixture.detectChanges();
let datePicker = fixture.debugElement.query(By.css(`[data-automation-id="datepicker-${component.property.key}"]`));
let datePickerToggle = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-${component.property.key}"]`));
expect(datePicker).not.toBeNull('Datepicker should be in DOM');
expect(datePickerToggle).not.toBeNull('Datepicker toggle should be shown');
});
it('should NOT render the picker and toggle in case of editable:false', () => {
component.property.editable = false;
fixture.detectChanges();
let datePicker = fixture.debugElement.query(By.css(`[data-automation-id="datepicker-${component.property.key}"]`));
let datePickerToggle = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-${component.property.key}"]`));
expect(datePicker).toBeNull('Datepicker should NOT be in DOM');
expect(datePickerToggle).toBeNull('Datepicker toggle 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 datePicker = fixture.debugElement.query(By.css(`[data-automation-id="datepicker-${component.property.key}"]`));
let datePickerToggle = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-${component.property.key}"]`));
expect(datePicker).toBeNull('Datepicker should NOT be in DOM');
expect(datePickerToggle).toBeNull('Datepicker toggle should NOT be shown');
});
it('should open the dateXXXpicker when clicking on the label', () => {
component.editable = true;
component.property.editable = true;
fixture.detectChanges();
spyOn(component.datepicker, 'open');
let datePickerLabelToggle = fixture.debugElement.query(By.css(`[data-automation-id="datepicker-label-toggle-${component.property.key}"]`));
datePickerLabelToggle.triggerEventHandler('click', {});
expect(component.datepicker.open).toHaveBeenCalled();
});
it('should trigger an update event on the CardViewUpdateService', (done) => {
component.editable = true;
component.property.editable = true;
const cardViewUpdateService = TestBed.get(CardViewUpdateService);
const expectedDate = moment('Jul 10 2017', 'MMM DD YY');
fixture.detectChanges();
cardViewUpdateService.itemUpdated$.subscribe(
(updateNotification) => {
expect(updateNotification.target).toBe(component.property);
expect(updateNotification.changed).toEqual({datekey: expectedDate.toDate()});
done();
}
);
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());
}
);
}));
});

View File

@@ -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 { Component, Input, OnInit, ViewChild } from '@angular/core';
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';
@Component({
providers: [
{ provide: DateAdapter, useClass: MomentDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS }],
selector: 'adf-card-view-dateitem',
templateUrl: './card-view-dateitem.component.html',
styleUrls: ['./card-view-dateitem.component.scss']
})
export class CardViewDateItemComponent implements OnInit {
public SHOW_FORMAT: string = 'MMM DD YY';
@Input()
property: CardViewDateItemModel;
@Input()
editable: boolean = false;
@Input()
displayEmpty: boolean = true;
@ViewChild(MatDatetimepicker)
public datepicker: MatDatetimepicker<any>;
valueDate: Moment;
constructor(private cardViewUpdateService: CardViewUpdateService,
private dateAdapter: DateAdapter<Moment>,
private preferences: UserPreferencesService) {
}
ngOnInit() {
this.preferences.locale$.subscribe((locale) => {
this.dateAdapter.setLocale(locale);
});
let momentDateAdapter = <MomentDateAdapter> this.dateAdapter;
momentDateAdapter.overrideDisplyaFormat = this.SHOW_FORMAT;
if (this.property.value) {
this.valueDate = moment(this.property.value, this.SHOW_FORMAT);
}
}
showProperty() {
return this.displayEmpty || !this.property.isEmpty();
}
isEditable() {
return this.editable && this.property.editable;
}
showDatePicker() {
this.datepicker.open();
}
onDateChanged(newDateValue) {
if (newDateValue) {
let momentDate = moment(newDateValue.value, this.SHOW_FORMAT, true);
if (momentDate.isValid()) {
this.valueDate = momentDate;
this.cardViewUpdateService.update(this.property, momentDate.toDate());
this.property.value = momentDate.toDate();
}
}
}
}