[ADF-833] DataTable - improve the single and double click event (#1979)

* Improve the single and double click event

* Fix unit test
This commit is contained in:
Maurizio Vitale
2017-06-19 13:16:54 +01:00
committed by Eugenio Romano
parent 3ca7503ddb
commit 4e7c5bfdbf
3 changed files with 142 additions and 68 deletions

View File

@@ -7,7 +7,6 @@
<th *ngIf="actions && actionsPosition === 'left'" class="alfresco-datatable__actions-header"> <th *ngIf="actions && actionsPosition === 'left'" class="alfresco-datatable__actions-header">
<span class="sr-only">Actions</span> <span class="sr-only">Actions</span>
</th> </th>
<!-- Columns --> <!-- Columns -->
<th *ngIf="multiselect"> <th *ngIf="multiselect">
<md-checkbox [checked]="isSelectAllChecked" (change)="onSelectAllClick($event)"></md-checkbox> <md-checkbox [checked]="isSelectAllChecked" (change)="onSelectAllClick($event)"></md-checkbox>
@@ -29,7 +28,6 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<ng-container *ngIf="!loading">
<tr *ngFor="let row of data.getRows(); let idx = index" tabindex="0" <tr *ngFor="let row of data.getRows(); let idx = index" tabindex="0"
class="alfresco-datatable__row" class="alfresco-datatable__row"
@@ -55,40 +53,38 @@
</ul> </ul>
</td> </td>
<td *ngIf="multiselect"> <td *ngIf="multiselect">
<md-checkbox [(ngModel)]="row.isSelected"></md-checkbox> <md-checkbox [(ngModel)]="row.isSelected"></md-checkbox>
</td> </td>
<td *ngFor="let col of data.getColumns()" <td *ngFor="let col of data.getColumns()"
class="mdl-data-table__cell--non-numeric non-selectable data-cell {{col.cssClass}}" class="mdl-data-table__cell--non-numeric non-selectable data-cell {{col.cssClass}}"
(click)="onRowClick(row, $event)" (click)="onRowClick(row, $event)"
(dblclick)="onRowDblClick(row, $event)" [context-menu]="getContextMenuActions(row, col)"
[context-menu]="getContextMenuActions(row, col)" [context-menu-enabled]="contextMenu">
[context-menu-enabled]="contextMenu"> <div *ngIf="!col.template" class="cell-container">
<div *ngIf="!col.template" class="cell-container"> <ng-container [ngSwitch]="col.type">
<ng-container [ngSwitch]="col.type"> <div *ngSwitchCase="'image'" class="cell-value">
<div *ngSwitchCase="'image'" class="cell-value"> <i *ngIf="isIconValue(row, col)" class="material-icons icon-cell">{{asIconValue(row, col)}}</i>
<i *ngIf="isIconValue(row, col)" class="material-icons icon-cell">{{asIconValue(row, <img *ngIf="!isIconValue(row, col)"
col)}}</i> class="image-cell"
<img *ngIf="!isIconValue(row, col)" alt="{{ iconAltTextKey(data.getValue(row, col)) | translate }}"
class="image-cell" src="{{ data.getValue(row, col) }}"
alt="{{ iconAltTextKey(data.getValue(row, col)) | translate }}" (error)="onImageLoadingError($event)">
src="{{ data.getValue(row, col) }}" </div>
(error)="onImageLoadingError($event)"> <div *ngSwitchCase="'icon'" class="cell-value">
</div> <img class="image-cell"
<div *ngSwitchCase="'icon'" class="cell-value"> alt="{{ iconAltTextKey(data.getValue(row, col)) | translate }}"
<img class="image-cell" src="{{ data.getValue(row, col) }}"
alt="{{ iconAltTextKey(data.getValue(row, col)) | translate }}" (error)="onImageLoadingError($event)">
src="{{ data.getValue(row, col) }}" </div>
(error)="onImageLoadingError($event)"> <div *ngSwitchCase="'date'" class="cell-value"
</div> [attr.data-automation-id]="'date_' + data.getValue(row, col)">
<div *ngSwitchCase="'date'" class="cell-value" <alfresco-datatable-cell [data]="data" [column]="col" [row]="row"></alfresco-datatable-cell>
[attr.data-automation-id]="'date_' + data.getValue(row, col)"> </div>
<alfresco-datatable-cell [data]="data" [column]="col" [row]="row"></alfresco-datatable-cell> <div *ngSwitchCase="'text'" class="cell-value"
</div> [attr.data-automation-id]="'text_' + data.getValue(row, col)">
<div *ngSwitchCase="'text'" class="cell-value" <alfresco-datatable-cell [data]="data" [column]="col" [row]="row"></alfresco-datatable-cell>
[attr.data-automation-id]="'text_' + data.getValue(row, col)"> </div>
<alfresco-datatable-cell [data]="data" [column]="col" [row]="row"></alfresco-datatable-cell>
</div>
<span *ngSwitchDefault class="cell-value"> <span *ngSwitchDefault class="cell-value">
<!-- empty cell for unknown column type --> <!-- empty cell for unknown column type -->
</span> </span>
@@ -129,8 +125,6 @@
</ng-template> </ng-template>
</td> </td>
</tr> </tr>
</ng-container>
<tr *ngIf="loading"> <tr *ngIf="loading">
<td class="mdl-data-table__cell--non-numeric adf-loading-content-container" <td class="mdl-data-table__cell--non-numeric adf-loading-content-container"
[attr.colspan]="1 + data.getColumns().length"> [attr.colspan]="1 + data.getColumns().length">

View File

@@ -101,6 +101,7 @@ describe('DataTable', () => {
); );
const rows = dataTable.data.getRows(); const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
dataTable.onRowClick(rows[0], null); dataTable.onRowClick(rows[0], null);
expect(rows[0].isSelected).toBeTruthy(); expect(rows[0].isSelected).toBeTruthy();
expect(rows[1].isSelected).toBeFalsy(); expect(rows[1].isSelected).toBeFalsy();
@@ -121,6 +122,7 @@ describe('DataTable', () => {
); );
const rows = dataTable.data.getRows(); const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
dataTable.onRowClick(rows[0], null); dataTable.onRowClick(rows[0], null);
expect(rows[0].isSelected).toBeTruthy(); expect(rows[0].isSelected).toBeTruthy();
expect(rows[1].isSelected).toBeFalsy(); expect(rows[1].isSelected).toBeFalsy();
@@ -145,6 +147,7 @@ describe('DataTable', () => {
metaKey: true metaKey: true
}); });
dataTable.ngOnChanges({});
dataTable.onRowClick(rows[0], event); dataTable.onRowClick(rows[0], event);
dataTable.onRowClick(rows[1], event); dataTable.onRowClick(rows[1], event);
@@ -212,18 +215,65 @@ describe('DataTable', () => {
done(); done();
}); });
dataTable.ngOnChanges({});
dataTable.onRowClick(row, null); dataTable.onRowClick(row, null);
}); });
it('should emit row double-click event', done => { it('should emit double click if there are two single click in 250ms', (done) => {
let row = <DataRow> {};
dataTable.rowDblClick.subscribe(e => { let row = <DataRow> {};
expect(e.value).toBe(row); dataTable.ngOnChanges({});
dataTable.rowDblClick.subscribe( () => {
done(); done();
}); });
dataTable.onRowDblClick(row, null); dataTable.onRowClick(row, null);
setTimeout(() => {
dataTable.onRowClick(row, null);
}
, 240);
});
it('should emit double click if there are more than two single click in 250ms', (done) => {
let row = <DataRow> {};
dataTable.ngOnChanges({});
dataTable.rowDblClick.subscribe( () => {
done();
});
dataTable.onRowClick(row, null);
setTimeout(() => {
dataTable.onRowClick(row, null);
dataTable.onRowClick(row, null);
}
, 240);
});
it('should emit single click if there are two single click in more than 250ms', (done) => {
let row = <DataRow> {};
let clickCount = 0;
dataTable.ngOnChanges({});
dataTable.rowClick.subscribe( () => {
clickCount += 1;
if (clickCount === 2) {
done();
}
});
dataTable.onRowClick(row, null);
setTimeout(() => {
dataTable.onRowClick(row, null);
}
, 260);
}); });
it('should emit row-click dom event', (done) => { it('should emit row-click dom event', (done) => {
@@ -234,6 +284,7 @@ describe('DataTable', () => {
done(); done();
}); });
dataTable.ngOnChanges({});
dataTable.onRowClick(row, null); dataTable.onRowClick(row, null);
}); });
@@ -244,8 +295,9 @@ describe('DataTable', () => {
expect(e.detail.value).toBe(row); expect(e.detail.value).toBe(row);
done(); done();
}); });
dataTable.ngOnChanges({});
dataTable.onRowDblClick(row, null); dataTable.onRowClick(row, null);
dataTable.onRowClick(row, null);
}); });
it('should prevent default behaviour on row click event', () => { it('should prevent default behaviour on row click event', () => {
@@ -257,6 +309,7 @@ describe('DataTable', () => {
it('should prevent default behaviour on row double-click event', () => { it('should prevent default behaviour on row double-click event', () => {
let e = jasmine.createSpyObj('event', ['preventDefault']); let e = jasmine.createSpyObj('event', ['preventDefault']);
dataTable.ngOnChanges({});
dataTable.ngAfterContentInit(); dataTable.ngAfterContentInit();
dataTable.onRowDblClick(null, e); dataTable.onRowDblClick(null, e);
expect(e.preventDefault).toHaveBeenCalled(); expect(e.preventDefault).toHaveBeenCalled();

View File

@@ -21,6 +21,7 @@ import { DataCellEvent } from './data-cell.event';
import { DataRowActionEvent } from './data-row-action.event'; import { DataRowActionEvent } from './data-row-action.event';
import { DataColumnListComponent } from 'ng2-alfresco-core'; import { DataColumnListComponent } from 'ng2-alfresco-core';
import { MdCheckboxChange } from '@angular/material'; import { MdCheckboxChange } from '@angular/material';
import { Observable, Observer } from 'rxjs/Rx';
declare var componentHandler; declare var componentHandler;
@@ -89,7 +90,11 @@ export class DataTableComponent implements AfterContentInit, OnChanges {
isSelectAllChecked: boolean = false; isSelectAllChecked: boolean = false;
private clickObserver: Observer<DataRowEvent>;
private click$: Observable<DataRowEvent>;
constructor(@Optional() private el: ElementRef) { constructor(@Optional() private el: ElementRef) {
this.click$ = new Observable<DataRowEvent>(observer => this.clickObserver = observer).share();
} }
ngAfterContentInit() { ngAfterContentInit() {
@@ -111,6 +116,7 @@ export class DataTableComponent implements AfterContentInit, OnChanges {
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
this.initAndSubscribeClickStream();
if (this.isPropertyChanged(changes['data'])) { if (this.isPropertyChanged(changes['data'])) {
if (this.isTableEmpty()) { if (this.isTableEmpty()) {
this.initTable(); this.initTable();
@@ -140,6 +146,46 @@ export class DataTableComponent implements AfterContentInit, OnChanges {
return rows.map(row => new ObjectDataRow(row)); return rows.map(row => new ObjectDataRow(row));
} }
private initAndSubscribeClickStream() {
let singleClickStream = this.click$
.buffer(this.click$.debounceTime(250))
.map(list => list)
.filter(x => x.length === 1);
singleClickStream.subscribe((obj: DataRowEvent[]) => {
let event: DataRowEvent = obj[0];
let el = obj[0].sender.el;
this.rowClick.emit(event);
if (!event.defaultPrevented && el.nativeElement) {
el.nativeElement.dispatchEvent(
new CustomEvent('row-click', {
detail: event,
bubbles: true
})
);
}
});
let multiClickStream = this.click$
.buffer(this.click$.debounceTime(250))
.map(list => list)
.filter(x => x.length >= 2);
multiClickStream.subscribe((obj: DataRowEvent[]) => {
let event: DataRowEvent = obj[0];
let el = obj[0].sender.el;
this.rowDblClick.emit(event);
if (!event.defaultPrevented && el.nativeElement) {
el.nativeElement.dispatchEvent(
new CustomEvent('row-dblclick', {
detail: event,
bubbles: true
})
);
}
});
}
private initTable() { private initTable() {
this.data = new ObjectDataTableAdapter(this.rows, []); this.data = new ObjectDataTableAdapter(this.rows, []);
} }
@@ -190,17 +236,8 @@ export class DataTableComponent implements AfterContentInit, OnChanges {
} }
} }
let event = new DataRowEvent(row, e, this); let dataRowEvent = new DataRowEvent(row, e, this);
this.rowClick.emit(event); this.clickObserver.next(dataRowEvent);
if (!event.defaultPrevented && this.el.nativeElement) {
this.el.nativeElement.dispatchEvent(
new CustomEvent('row-click', {
detail: event,
bubbles: true
})
);
}
} }
} }
@@ -217,18 +254,8 @@ export class DataTableComponent implements AfterContentInit, OnChanges {
if (e) { if (e) {
e.preventDefault(); e.preventDefault();
} }
let dataRowEvent = new DataRowEvent(row, e, this);
let event = new DataRowEvent(row, e, this); this.clickObserver.next(dataRowEvent);
this.rowDblClick.emit(event);
if (!event.defaultPrevented && this.el.nativeElement) {
this.el.nativeElement.dispatchEvent(
new CustomEvent('row-dblclick', {
detail: event,
bubbles: true
})
);
}
} }
onColumnHeaderClick(column: DataColumn) { onColumnHeaderClick(column: DataColumn) {