[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-->
- [adf-card-view](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-dateitem](ng2-alfresco-core/README.md)
- [adf-context-menu-holder](ng2-alfresco-core/README.md)

View File

@@ -27,7 +27,7 @@ import { Component,
ViewChild
} from '@angular/core';
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 { TaskDetailsModel } from '../models/task-details.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.itemClicked$.subscribe(this.clickTaskDetails.bind(this));
}
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
* @param taskId

View File

@@ -16,7 +16,7 @@
*/
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 { TaskListService } from './../services/tasklist.service';
@@ -54,12 +54,13 @@ export class TaskHeaderComponent implements OnChanges {
refreshData() {
if (this.taskDetails) {
let valueMap = new Map([[this.taskDetails.processDefinitionId, this.taskDetails.processDefinitionName]])
this.properties = [
new CardViewTextItemModel({ label: 'Assignee', value: this.taskDetails.getFullName(), key: 'assignee', default: 'No assignee' } ),
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 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 CardViewDateItemModel({ label: 'Created', value: this.taskDetails.created, key: 'created' }),
new CardViewTextItemModel({ label: 'Id', value: this.taskDetails.id, key: 'id' }),

View File

@@ -33,6 +33,8 @@
* [Defining properties](#defining-properties)
* [Card Text Item](#card-text-item)
+ [Options](#options)
* [Card Map Item](#card-map-item)
+ [Options](#options)
* [Card Date Item](#card-date-item)
+ [Options](#options-1)
* [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*
- **[CardViewMapItemModel](#card-map-item)** - *for map 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.
@@ -661,6 +664,13 @@ Each of them are extending the abstract CardViewBaseItemModel class, and each of
default: 'default bar' ,
multiline: false
}),
new CardViewMapItemModel({
label: 'My map',
value: new Map([['999', 'My Value']]),
key: 'map',
default: 'default map value' ,
clickable: true
}),
new CardViewDateItemModel({
label: 'Birth of date',
value: someDate,
@@ -691,8 +701,28 @@ const textItemProperty = new CardViewTextItemModel(options);
| default | any | --- | The default value to render in case the value is empty |
| displayValue* | string | --- | The value to render |
| 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 |
### 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
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 { CardViewUpdateService } 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 { 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/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/file.model';
export * from './src/models/permissions.enum';

View File

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

View File

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

View File

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

View File

@@ -47,6 +47,10 @@ export class CardViewTextItemComponent implements OnChanges {
return this.editable && this.property.editable;
}
isClickable() {
return this.property.clickable;
}
setEditMode(editStatus: boolean): void {
this.inEdit = editStatus;
setTimeout(() => {
@@ -62,4 +66,8 @@ export class CardViewTextItemComponent implements OnChanges {
update(): void {
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 { CardViewItemDispatcherComponent } from './card-view-item-dispatcher.component';
import { CardViewTextItemComponent } from './card-view-textitem.component';
import { CardViewMapItemComponent } from './card-view-mapitem.component';
import { CardViewComponent } from './card-view.component';
@NgModule({
@@ -40,10 +41,12 @@ import { CardViewComponent } from './card-view.component';
CardViewItemDispatcherComponent,
CardViewContentProxyDirective,
CardViewTextItemComponent,
CardViewMapItemComponent,
CardViewDateItemComponent
],
entryComponents: [
CardViewTextItemComponent,
CardViewMapItemComponent,
CardViewDateItemComponent
],
exports: [

View File

@@ -29,6 +29,7 @@ export interface CardViewItemProperties {
key: any;
default?: string;
editable?: boolean;
clickable?: boolean;
}
export abstract class CardViewBaseItemModel {
@@ -37,6 +38,7 @@ export abstract class CardViewBaseItemModel {
key: any;
default: string;
editable: boolean;
clickable: boolean;
constructor(obj: CardViewItemProperties) {
this.label = obj.label || '';
@@ -44,5 +46,6 @@ export abstract class CardViewBaseItemModel {
this.key = obj.key;
this.default = obj.default;
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;
}
export interface ClickNotification {
target: any;
}
@Injectable()
export class CardViewUpdateService {
@@ -33,10 +37,18 @@ export class CardViewUpdateService {
// Observable streams
public itemUpdated$ = this.itemUpdatedSource.asObservable();
public itemClicked$: Subject<ClickNotification> = new Subject<ClickNotification>();
update(property: CardViewBaseItemModel, changed: any) {
this.itemUpdatedSource.next({
target: property,
changed
});
}
clicked(property: CardViewBaseItemModel) {
this.itemClicked$.next({
target: property
});
}
}