[ADF-1108] Task header - Show the parent name as a clickable value (#2098)

* Add a MapItem component inside the CardVied
Add a way to define a property clickable

* Fix unit test

* Add basic documentation
Unify css class name

* Fix class name

* remove unused class
This commit is contained in:
Maurizio Vitale
2017-07-19 16:22:32 +01:00
committed by Eugenio Romano
parent 1214c2ebab
commit 33fc2373ae
18 changed files with 319 additions and 21 deletions

View File

@@ -24,6 +24,7 @@
<!-- CORE START--> <!-- CORE START-->
- [adf-card-view](ng2-alfresco-core/README.md) - [adf-card-view](ng2-alfresco-core/README.md)
- [adf-card-view-textitem](ng2-alfresco-core/README.md) - [adf-card-view-textitem](ng2-alfresco-core/README.md)
- [adf-card-view-mapitem](ng2-alfresco-core/README.md)
- [adf-card-view-item-dispatcher](ng2-alfresco-core/README.md) - [adf-card-view-item-dispatcher](ng2-alfresco-core/README.md)
- [adf-card-view-dateitem](ng2-alfresco-core/README.md) - [adf-card-view-dateitem](ng2-alfresco-core/README.md)
- [adf-context-menu-holder](ng2-alfresco-core/README.md) - [adf-context-menu-holder](ng2-alfresco-core/README.md)

View File

@@ -27,7 +27,7 @@ import { Component,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { ContentLinkModel, FormModel, FormOutcomeEvent, FormService } from 'ng2-activiti-form'; import { ContentLinkModel, FormModel, FormOutcomeEvent, FormService } from 'ng2-activiti-form';
import { AlfrescoAuthenticationService, AlfrescoTranslationService, CardViewUpdateService, LogService, UpdateNotification } from 'ng2-alfresco-core'; import { AlfrescoAuthenticationService, AlfrescoTranslationService, CardViewUpdateService, LogService, UpdateNotification, ClickNotification } from 'ng2-alfresco-core';
import { TaskQueryRequestRepresentationModel } from '../models/filter.model'; import { TaskQueryRequestRepresentationModel } from '../models/filter.model';
import { TaskDetailsModel } from '../models/task-details.model'; import { TaskDetailsModel } from '../models/task-details.model';
import { User } from '../models/user.model'; import { User } from '../models/user.model';
@@ -161,6 +161,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
} }
this.cardViewUpdateService.itemUpdated$.subscribe(this.updateTaskDetails.bind(this)); this.cardViewUpdateService.itemUpdated$.subscribe(this.updateTaskDetails.bind(this));
this.cardViewUpdateService.itemClicked$.subscribe(this.clickTaskDetails.bind(this));
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
@@ -206,6 +207,10 @@ export class TaskDetailsComponent implements OnInit, OnChanges {
); );
} }
private clickTaskDetails(clickNotification: ClickNotification) {
console.log(clickNotification.target);
}
/** /**
* Load the activiti task details * Load the activiti task details
* @param taskId * @param taskId

View File

@@ -16,7 +16,7 @@
*/ */
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService, CardViewDateItemModel, CardViewItem, CardViewTextItemModel, LogService } from 'ng2-alfresco-core'; import { AlfrescoTranslationService, CardViewDateItemModel, CardViewItem, CardViewTextItemModel, CardViewMapItemModel, LogService } from 'ng2-alfresco-core';
import { TaskDetailsModel } from '../models/task-details.model'; import { TaskDetailsModel } from '../models/task-details.model';
import { TaskListService } from './../services/tasklist.service'; import { TaskListService } from './../services/tasklist.service';
@@ -54,12 +54,13 @@ export class TaskHeaderComponent implements OnChanges {
refreshData() { refreshData() {
if (this.taskDetails) { if (this.taskDetails) {
let valueMap = new Map([[this.taskDetails.processDefinitionId, this.taskDetails.processDefinitionName]])
this.properties = [ this.properties = [
new CardViewTextItemModel({ label: 'Assignee', value: this.taskDetails.getFullName(), key: 'assignee', default: 'No assignee' } ), new CardViewTextItemModel({ label: 'Assignee', value: this.taskDetails.getFullName(), key: 'assignee', default: 'No assignee' } ),
new CardViewTextItemModel({ label: 'Status', value: this.getTaskStatus(), key: 'status' }), new CardViewTextItemModel({ label: 'Status', value: this.getTaskStatus(), key: 'status' }),
new CardViewDateItemModel({ label: 'Due Date', value: this.taskDetails.dueDate, key: 'dueDate', default: 'No date', editable: true }), new CardViewDateItemModel({ label: 'Due Date', value: this.taskDetails.dueDate, key: 'dueDate', default: 'No date', editable: true }),
new CardViewTextItemModel({ label: 'Category', value: this.taskDetails.category, key: 'category', default: 'No category' }), new CardViewTextItemModel({ label: 'Category', value: this.taskDetails.category, key: 'category', default: 'No category' }),
new CardViewMapItemModel({ label: 'Parent name', value: valueMap, key: 'parentName', default: 'No parent name', clickable: true }),
new CardViewTextItemModel({ label: 'Created By', value: this.taskDetails.getFullName(), key: 'created-by', default: 'No assignee' }), new CardViewTextItemModel({ label: 'Created By', value: this.taskDetails.getFullName(), key: 'created-by', default: 'No assignee' }),
new CardViewDateItemModel({ label: 'Created', value: this.taskDetails.created, key: 'created' }), new CardViewDateItemModel({ label: 'Created', value: this.taskDetails.created, key: 'created' }),
new CardViewTextItemModel({ label: 'Id', value: this.taskDetails.id, key: 'id' }), new CardViewTextItemModel({ label: 'Id', value: this.taskDetails.id, key: 'id' }),

View File

@@ -33,6 +33,8 @@
* [Defining properties](#defining-properties) * [Defining properties](#defining-properties)
* [Card Text Item](#card-text-item) * [Card Text Item](#card-text-item)
+ [Options](#options) + [Options](#options)
* [Card Map Item](#card-map-item)
+ [Options](#options)
* [Card Date Item](#card-date-item) * [Card Date Item](#card-date-item)
+ [Options](#options-1) + [Options](#options-1)
* [Defining your custom card Item](#defining-your-custom-card-item) * [Defining your custom card Item](#defining-your-custom-card-item)
@@ -645,9 +647,10 @@ export interface CardViewItem {
} }
``` ```
At the moment two models are defined out of the box: At the moment three models are defined out of the box:
- **[CardViewTextItemModel](#card-text-item)** - *for text items* - **[CardViewTextItemModel](#card-text-item)** - *for text items*
- **[CardViewMapItemModel](#card-map-item)** - *for map items*
- **[CardViewDateItemModel](#card-date-item)** - *for date items* - **[CardViewDateItemModel](#card-date-item)** - *for date items*
Each of them are extending the abstract CardViewBaseItemModel class, and each of them are adding some custom functionality to the basic behaviour. Each of them are extending the abstract CardViewBaseItemModel class, and each of them are adding some custom functionality to the basic behaviour.
@@ -661,6 +664,13 @@ Each of them are extending the abstract CardViewBaseItemModel class, and each of
default: 'default bar' , default: 'default bar' ,
multiline: false multiline: false
}), }),
new CardViewMapItemModel({
label: 'My map',
value: new Map([['999', 'My Value']]),
key: 'map',
default: 'default map value' ,
clickable: true
}),
new CardViewDateItemModel({ new CardViewDateItemModel({
label: 'Birth of date', label: 'Birth of date',
value: someDate, value: someDate,
@@ -691,8 +701,28 @@ const textItemProperty = new CardViewTextItemModel(options);
| default | any | --- | The default value to render in case the value is empty | | default | any | --- | The default value to render in case the value is empty |
| displayValue* | string | --- | The value to render | | displayValue* | string | --- | The value to render |
| editable | boolean | false | Whether the property editable or not | | editable | boolean | false | Whether the property editable or not |
| clickable | boolean | false | Whether the property clikable or not |
| multiline | string | false | Single or multiline text | | multiline | string | false | Single or multiline text |
### Card Map Item
CardViewMapItemModel is a property type for map properties.
```js
const mapItemProperty = new CardViewMapItemModel(options);
```
#### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| label* | string | --- | The label to render |
| value* | Map | --- | A map that contains the key value pairs |
| key* | string | --- | the key of the property. Have an important role when editing the property. |
| default | any | --- | The default value to render in case the value is empty |
| displayValue* | string | --- | The value to render |
| clickable | boolean | false | Whether the property clickable or not |
### Card Date Item ### Card Date Item
CardViewDateItemModel is a property type for date properties. CardViewDateItemModel is a property type for date properties.

View File

@@ -82,6 +82,7 @@ export { ThumbnailService } from './src/services/thumbnail.service';
export { UploadService } from './src/services/upload.service'; export { UploadService } from './src/services/upload.service';
export { CardViewUpdateService } from './src/services/card-view-update.service'; export { CardViewUpdateService } from './src/services/card-view-update.service';
export { UpdateNotification } from './src/services/card-view-update.service'; export { UpdateNotification } from './src/services/card-view-update.service';
export { ClickNotification } from './src/services/card-view-update.service';
export { AppConfigModule } from './src/services/app-config.service'; export { AppConfigModule } from './src/services/app-config.service';
export { UserPreferencesService } from './src/services/user-preferences.service'; export { UserPreferencesService } from './src/services/user-preferences.service';
@@ -119,6 +120,7 @@ export * from './src/events/folder-created.event';
export * from './src/events/file.event'; export * from './src/events/file.event';
export * from './src/models/card-view-textitem.model'; export * from './src/models/card-view-textitem.model';
export * from './src/models/card-view-mapitem.model';
export * from './src/models/card-view-dateitem.model'; export * from './src/models/card-view-dateitem.model';
export * from './src/models/file.model'; export * from './src/models/file.model';
export * from './src/models/permissions.enum'; export * from './src/models/permissions.enum';

View File

@@ -46,18 +46,14 @@ describe('CardViewDateItemComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(CardViewDateItemComponent); fixture = TestBed.createComponent(CardViewDateItemComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.property = <CardViewDateItemModel> { component.property = new CardViewDateItemModel ({
type: 'date',
label: 'Date label', label: 'Date label',
value: new Date('07/10/2017'), value: new Date('07/10/2017'),
key: 'datekey', key: 'datekey',
default: '', default: '',
format: '', format: '',
editable: false, editable: false
get displayValue(): string { });
return 'Jul 10 2017';
}
};
}); });
afterEach(() => { afterEach(() => {

View File

@@ -0,0 +1,9 @@
<div class="adf-property-label">{{ property.label }}</div>
<div class="adf-property-value">
<div>
<span *ngIf="!isClickable(); else elseBlock" [attr.data-automation-id]="'card-mapitem-value-' + property.key">{{ property.displayValue }}</span>
<ng-template #elseBlock>
<span class="adf-mapitem-clickable-value" (click)="clicked()" [attr.data-automation-id]="'card-mapitem-value-' + property.key">{{ property.displayValue }}</span>
</ng-template>
</div>
</div>

View File

@@ -0,0 +1,7 @@
@import 'theming';
.#{$ADF} {
&-mapitem-clickable-value {
cursor: pointer;
}
}

View File

@@ -0,0 +1,129 @@
/*!
* @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 { DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { MdDatepickerModule, MdIconModule, MdInputModule, MdNativeDateModule } from '@angular/material';
import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CardViewMapItemModel } from '../../models/card-view-mapitem.model';
import { CardViewUpdateService } from '../../services/card-view-update.service';
import { CardViewMapItemComponent } from './card-view-mapitem.component';
describe('CardViewMapItemComponent', () => {
let service: CardViewUpdateService;
let fixture: ComponentFixture<CardViewMapItemComponent>;
let component: CardViewMapItemComponent;
let debug: DebugElement;
let element: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
NoopAnimationsModule,
MdDatepickerModule,
MdIconModule,
MdInputModule,
MdNativeDateModule
],
declarations: [
CardViewMapItemComponent
],
providers: [
CardViewUpdateService
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CardViewMapItemComponent);
service = TestBed.get(CardViewUpdateService);
component = fixture.componentInstance;
debug = fixture.debugElement;
element = fixture.nativeElement;
});
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
it('should render the default if the value is empty', () => {
component.property = new CardViewMapItemModel({
label: 'Map label',
value: null,
key: 'mapkey',
default: 'Fake default'
});
fixture.detectChanges();
let labelValue = debug.query(By.css('.adf-property-label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('Map label');
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('Fake default');
});
it('should render the label and value', () => {
component.property = new CardViewMapItemModel({
label: 'Map label',
value: new Map([['999', 'fakeProcessName']]),
key: 'mapkey',
default: ''
});
fixture.detectChanges();
let labelValue = debug.query(By.css('.adf-property-label'));
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('Map label');
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('fakeProcessName');
});
it('should render a clickable value', (done) => {
component.property = new CardViewMapItemModel({
label: 'Map label',
value: new Map([['999', 'fakeProcessName']]),
key: 'mapkey',
default: 'Fake default',
clickable: true
});
fixture.detectChanges();
let value: any = element.querySelector('.adf-mapitem-clickable-value');
service.itemClicked$.subscribe((response) => {
expect(response.target).not.toBeNull();
expect(response.target.type).toEqual('map');
expect(response.target.clickable).toBeTruthy();
expect(response.target.displayValue).toEqual('fakeProcessName');
done();
});
value.click();
});
});

View File

@@ -0,0 +1,45 @@
/*!
* @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, OnChanges } from '@angular/core';
import { CardViewMapItemModel } from '../../models/card-view-mapitem.model';
import { CardViewUpdateService } from '../../services/card-view-update.service';
@Component({
selector: 'adf-card-view-mapitem',
templateUrl: './card-view-mapitem.component.html',
styleUrls: ['./card-view-mapitem.component.scss']
})
export class CardViewMapItemComponent implements OnChanges {
@Input()
property: CardViewMapItemModel;
constructor(private cardViewUpdateService: CardViewUpdateService) {}
ngOnChanges() {
console.log();
}
isClickable() {
return this.property.clickable;
}
clicked(): void {
this.cardViewUpdateService.clicked(this.property);
}
}

View File

@@ -1,7 +1,10 @@
<div class="adf-property-label">{{ property.label }}</div> <div class="adf-property-label">{{ property.label }}</div>
<div class="adf-property-value"> <div class="adf-property-value">
<span *ngIf="!isEditble()"> <span *ngIf="!isEditble()">
<span [attr.data-automation-id]="'card-textitem-value-' + property.key">{{ property.displayValue }}</span> <span *ngIf="!isClickable(); else elseBlock" [attr.data-automation-id]="'card-textitem-value-' + property.key">{{ property.displayValue }}</span>
<ng-template #elseBlock>
<span class="adf-textitem-clickable-value" (click)="clicked()" [attr.data-automation-id]="'card-textitem-value-' + property.key">{{ property.displayValue }}</span>
</ng-template>
</span> </span>
<span *ngIf="isEditble()"> <span *ngIf="isEditble()">
<div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key"> <div *ngIf="!inEdit" (click)="setEditMode(true)" class="adf-textitem-readonly" [attr.data-automation-id]="'card-textitem-edit-toggle-' + property.key">
@@ -36,4 +39,4 @@
[attr.data-automation-id]="'card-textitem-reset-' + property.key">clear</md-icon> [attr.data-automation-id]="'card-textitem-reset-' + property.key">clear</md-icon>
</div> </div>
</span> </span>
</div> </div>

View File

@@ -23,6 +23,10 @@
} }
} }
&-textitem-clickable-value {
cursor: pointer;
}
&-textitem-editable { &-textitem-editable {
display: flex; display: flex;
@@ -77,4 +81,4 @@
&-textitem-editable /deep/ input.mat-input-element:focus { &-textitem-editable /deep/ input.mat-input-element:focus {
margin-bottom: -7px; margin-bottom: -7px;
} }
} }

View File

@@ -51,17 +51,13 @@ describe('CardViewTextItemComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(CardViewTextItemComponent); fixture = TestBed.createComponent(CardViewTextItemComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.property = <CardViewTextItemModel> { component.property = new CardViewTextItemModel ({
type: 'text',
label: 'Text label', label: 'Text label',
value: 'Lorem ipsum', value: 'Lorem ipsum',
key: 'textkey', key: 'textkey',
default: '', default: '',
editable: false, editable: false
get displayValue(): string { });
return 'Lorem ipsum';
}
};
}); });
afterEach(() => { afterEach(() => {

View File

@@ -47,6 +47,10 @@ export class CardViewTextItemComponent implements OnChanges {
return this.editable && this.property.editable; return this.editable && this.property.editable;
} }
isClickable() {
return this.property.clickable;
}
setEditMode(editStatus: boolean): void { setEditMode(editStatus: boolean): void {
this.inEdit = editStatus; this.inEdit = editStatus;
setTimeout(() => { setTimeout(() => {
@@ -62,4 +66,8 @@ export class CardViewTextItemComponent implements OnChanges {
update(): void { update(): void {
this.cardViewUpdateService.update(this.property, { [this.property.key]: this.editedValue }); this.cardViewUpdateService.update(this.property, { [this.property.key]: this.editedValue });
} }
clicked(): void {
this.cardViewUpdateService.clicked(this.property);
}
} }

View File

@@ -23,6 +23,7 @@ import { CardViewContentProxyDirective } from './card-view-content-proxy.directi
import { CardViewDateItemComponent } from './card-view-dateitem.component'; import { CardViewDateItemComponent } from './card-view-dateitem.component';
import { CardViewItemDispatcherComponent } from './card-view-item-dispatcher.component'; import { CardViewItemDispatcherComponent } from './card-view-item-dispatcher.component';
import { CardViewTextItemComponent } from './card-view-textitem.component'; import { CardViewTextItemComponent } from './card-view-textitem.component';
import { CardViewMapItemComponent } from './card-view-mapitem.component';
import { CardViewComponent } from './card-view.component'; import { CardViewComponent } from './card-view.component';
@NgModule({ @NgModule({
@@ -40,10 +41,12 @@ import { CardViewComponent } from './card-view.component';
CardViewItemDispatcherComponent, CardViewItemDispatcherComponent,
CardViewContentProxyDirective, CardViewContentProxyDirective,
CardViewTextItemComponent, CardViewTextItemComponent,
CardViewMapItemComponent,
CardViewDateItemComponent CardViewDateItemComponent
], ],
entryComponents: [ entryComponents: [
CardViewTextItemComponent, CardViewTextItemComponent,
CardViewMapItemComponent,
CardViewDateItemComponent CardViewDateItemComponent
], ],
exports: [ exports: [

View File

@@ -29,6 +29,7 @@ export interface CardViewItemProperties {
key: any; key: any;
default?: string; default?: string;
editable?: boolean; editable?: boolean;
clickable?: boolean;
} }
export abstract class CardViewBaseItemModel { export abstract class CardViewBaseItemModel {
@@ -37,6 +38,7 @@ export abstract class CardViewBaseItemModel {
key: any; key: any;
default: string; default: string;
editable: boolean; editable: boolean;
clickable: boolean;
constructor(obj: CardViewItemProperties) { constructor(obj: CardViewItemProperties) {
this.label = obj.label || ''; this.label = obj.label || '';
@@ -44,5 +46,6 @@ export abstract class CardViewBaseItemModel {
this.key = obj.key; this.key = obj.key;
this.default = obj.default; this.default = obj.default;
this.editable = obj.editable || false; this.editable = obj.editable || false;
this.clickable = obj.clickable || false;
} }
} }

View File

@@ -0,0 +1,44 @@
/*!
* @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.
*/
/**
*
* This object represent the basic structure of a card view.
*
*
* @returns {CardViewMapItemModel} .
*/
import { CardViewItem } from '../interface/card-view-item.interface';
import { CardViewBaseItemModel, CardViewItemProperties } from './card-view-baseitem.model';
export class CardViewMapItemModel extends CardViewBaseItemModel implements CardViewItem {
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;
} else {
return this.default;
}
}
}

View File

@@ -24,6 +24,10 @@ export interface UpdateNotification {
changed: any; changed: any;
} }
export interface ClickNotification {
target: any;
}
@Injectable() @Injectable()
export class CardViewUpdateService { export class CardViewUpdateService {
@@ -33,10 +37,18 @@ export class CardViewUpdateService {
// Observable streams // Observable streams
public itemUpdated$ = this.itemUpdatedSource.asObservable(); public itemUpdated$ = this.itemUpdatedSource.asObservable();
public itemClicked$: Subject<ClickNotification> = new Subject<ClickNotification>();
update(property: CardViewBaseItemModel, changed: any) { update(property: CardViewBaseItemModel, changed: any) {
this.itemUpdatedSource.next({ this.itemUpdatedSource.next({
target: property, target: property,
changed changed
}); });
} }
clicked(property: CardViewBaseItemModel) {
this.itemClicked$.next({
target: property
});
}
} }