diff --git a/.travis.yml b/.travis.yml index 38bd43820e..011fd89d91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,8 @@ before_install: - export CHROME_BIN=/usr/bin/google-chrome - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - - (cd ng2-components/ng2-alfresco-core; npm version patch; sed -i "s/0\\.0\\.0-PLACEHOLDER/^0.1.0/g" package.json; npm install; npm link) + - (cd ng2-components/ng2-alfresco-core; npm install; npm link) + - (cd ng2-components/ng2-alfresco-datatable; npm install; npm link) env: matrix: @@ -30,7 +31,7 @@ env: - MODULE=ng2-alfresco-viewer before_script: - - cd ng2-components/$MODULE; npm version patch; sed -i "s/0\\.0\\.0-PLACEHOLDER/^0.1.0/g" package.json; npm install; npm link ng2-alfresco-core; npm run travis + - cd ng2-components/$MODULE; npm install; npm link ng2-alfresco-core; npm run travis - ls -ltrh ./node_modules/ script: npm run test # Send coverage data to Coveralls diff --git a/demo-shell-ng2/.gitignore b/demo-shell-ng2/.gitignore index fd79f1c9c2..964913e5ef 100644 --- a/demo-shell-ng2/.gitignore +++ b/demo-shell-ng2/.gitignore @@ -4,4 +4,8 @@ bower_components/ app/**/*.js app/**/*.js.map .idea -dist/ \ No newline at end of file +dist/ + +# docker files +docker-compose.yml +Dockerfile diff --git a/demo-shell-ng2/app/components/files/files.component.html b/demo-shell-ng2/app/components/files/files.component.html index 4b85525635..0194179160 100644 --- a/demo-shell-ng2/app/components/files/files.component.html +++ b/demo-shell-ng2/app/components/files/files.component.html @@ -10,6 +10,8 @@ - + @@ -50,25 +55,16 @@ - - @@ -76,37 +72,21 @@ - - - - diff --git a/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.spec.ts b/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.spec.ts index dc96f1f3d0..7c1e56ccb9 100644 --- a/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.spec.ts +++ b/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.spec.ts @@ -34,6 +34,7 @@ describe('ContextMenuDirective', () => { done(); }); + directive.links = [{}]; directive.onShowContextMenu(null); }); diff --git a/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.ts b/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.ts index ae816ce374..be498b8069 100644 --- a/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.ts +++ b/ng2-components/ng2-alfresco-core/src/components/context-menu.directive.ts @@ -26,17 +26,20 @@ import { ContextMenuService } from './../services/context-menu.service'; }) export class ContextMenuDirective { @Input('context-menu') - links; + links: any[]; constructor( private _contextMenuService: ContextMenuService) {} onShowContextMenu(event?: MouseEvent) { - if (this._contextMenuService) { - this._contextMenuService.show.next({event: event, obj: this.links}); - } if (event) { event.preventDefault(); } + + if (this.links && this.links.length > 0) { + if (this._contextMenuService) { + this._contextMenuService.show.next({event: event, obj: this.links}); + } + } } } diff --git a/ng2-components/ng2-alfresco-datatable/index.ts b/ng2-components/ng2-alfresco-datatable/index.ts index e1791a1457..2c4b5b3e3d 100644 --- a/ng2-components/ng2-alfresco-datatable/index.ts +++ b/ng2-components/ng2-alfresco-datatable/index.ts @@ -16,14 +16,17 @@ */ import { DataTableComponent } from './src/components/datatable.component'; +import { NoContentTemplateComponent } from './src/components/no-content-template.component'; // components export * from './src/components/datatable.component'; +export * from './src/components/no-content-template.component'; // data export * from './src/data/datatable-adapter'; export * from './src/data/object-datatable-adapter'; export const ALFRESCO_DATATABLE_DIRECTIVES: [any] = [ - DataTableComponent + DataTableComponent, + NoContentTemplateComponent ]; diff --git a/ng2-components/ng2-alfresco-datatable/karma-test-shim.js b/ng2-components/ng2-alfresco-datatable/karma-test-shim.js index 25bae3ce0a..2d378ec947 100644 --- a/ng2-components/ng2-alfresco-datatable/karma-test-shim.js +++ b/ng2-components/ng2-alfresco-datatable/karma-test-shim.js @@ -9,13 +9,15 @@ var map = { 'app': 'base/dist', 'rxjs': 'base/node_modules/rxjs', '@angular': 'base/node_modules/@angular', - 'ng2-alfresco-core/dist': '/base/node_modules/ng2-alfresco-core/dist' + 'ng2-alfresco-core': '/base/node_modules/ng2-alfresco-core/dist', + 'ng2-translate' : '/base/node_modules/ng2-translate' }; var packages = { 'app': { main: 'main.js', defaultExtension: 'js' }, 'rxjs': { defaultExtension: 'js' }, - 'ng2-alfresco-core/dist': { defaultExtension: 'js' } + 'ng2-alfresco-core': { main: 'index.js', defaultExtension: 'js' }, + 'ng2-translate': { defaultExtension: 'js' } }; var packageNames = [ diff --git a/ng2-components/ng2-alfresco-datatable/karma.conf.js b/ng2-components/ng2-alfresco-datatable/karma.conf.js index 8fc9946c73..d875822935 100644 --- a/ng2-components/ng2-alfresco-datatable/karma.conf.js +++ b/ng2-components/ng2-alfresco-datatable/karma.conf.js @@ -16,6 +16,7 @@ module.exports = function (config) { {pattern: 'node_modules/@angular/**/*.js', included: false, watched: false}, {pattern: 'node_modules/@angular/**/*.map', included: false, watched: false}, {pattern: 'node_modules/ng2-alfresco-core/dist/**/*.js', included: false, served: true, watched: false}, + {pattern: 'node_modules/ng2-translate/**/*.js', included: false, served: true, watched: false}, {pattern: 'karma-test-shim.js', included: true, watched: true}, diff --git a/ng2-components/ng2-alfresco-datatable/package.json b/ng2-components/ng2-alfresco-datatable/package.json index 564bf175c4..e810790c48 100644 --- a/ng2-components/ng2-alfresco-datatable/package.json +++ b/ng2-components/ng2-alfresco-datatable/package.json @@ -27,7 +27,7 @@ "posttest": "node_modules/.bin/remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html", "coverage": "npm run test && wsrv -o -p 9875 ./coverage/report", "prepublish": "npm run build", - "travis": "echo 'placeholder'" + "travis": "npm link ng2-alfresco-core" }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", @@ -67,7 +67,9 @@ "reflect-metadata": "0.1.3", "rxjs": "5.0.0-beta.6", "zone.js": "0.6.12", - "rimraf": "2.5.2" + "rimraf": "2.5.2", + "ng2-translate": "2.2.2", + "ng2-alfresco-core": "0.2.0" }, "peerDependencies": { "material-design-icons": "^2.2.3", diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.css b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.css index 1fddcdca8c..415a6264c9 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.css +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.css @@ -14,11 +14,58 @@ :host .data-cell { cursor: default; } +:host .cell-value {} :host .column-header { cursor: pointer; + user-select: none; + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE/Edge */ + -webkit-touch-callout: none; /* iOS Safari */ } +/* Empty folder */ + +:host .no-content-container { + padding: 0 !important; +} + +:host .no-content-container > img { + width: 100%; +} + +:host .ellipsis-cell > div +{ + position: relative; + overflow: hidden; + /*height: 1em;*/ +} + + +/* visible content */ +:host .ellipsis-cell > div > span +{ + display: block; + position: absolute; + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1em; /* for vertical align of text */ +} + + +/* cell stretching content */ +:host .ellipsis-cell > div:after +{ + content: attr(title); + overflow: hidden; + height: 0; + display: block; +} + + /* Utils */ :host .non-selectable { @@ -39,3 +86,22 @@ clip: rect(0,0,0,0); border: 0; } + +/* small desktop */ +@media all and (max-width: 1200px) {} + +/* tablet */ +@media all and (max-width: 1024px) {} + +/* mobile phone */ +@media all and (max-width: 768px) { + .desktop-only { + display: none; + } +} + +@media (max-device-width: 768px){ + .desktop-only { + display: none; + } +} diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.html b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.html index 6bee3e7145..d7e3dd9bbb 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.html +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.html @@ -15,6 +15,7 @@ -
+ (click)="onRowClick(row, $event)" + (dblclick)="onRowDblClick(row, $event)" + [context-menu]="getContextMenuActions(row, col)"> +
{{asIconValue(row, col)}}
-
+
{{data.getValue(row, col)}}
- +
+ {{data.getValue(row, col)}} +
+ - + + + +
    +
  • + {{action.title}} +
  • +
+ + + + + + diff --git a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts index 2ef163b267..e082904987 100644 --- a/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts +++ b/ng2-components/ng2-alfresco-datatable/src/components/datatable.component.ts @@ -22,14 +22,18 @@ import { Input, Output, EventEmitter, - AfterViewChecked + AfterViewChecked, + TemplateRef } from '@angular/core'; +import { CONTEXT_MENU_DIRECTIVES } from 'ng2-alfresco-core'; + import { DataTableAdapter, DataRow, DataColumn, - DataSorting + DataSorting, + DataRowEvent } from './../data/datatable-adapter'; import { ObjectDataTableAdapter } from '../data/object-datatable-adapter'; @@ -40,7 +44,8 @@ declare let __moduleName: string; moduleId: __moduleName, selector: 'alfresco-datatable', styleUrls: ['./datatable.component.css'], - templateUrl: './datatable.component.html' + templateUrl: './datatable.component.html', + directives: [CONTEXT_MENU_DIRECTIVES] }) export class DataTableComponent implements OnInit, AfterViewChecked { @@ -54,13 +59,24 @@ export class DataTableComponent implements OnInit, AfterViewChecked { actions: boolean = false; @Output() - rowClick: EventEmitter = new EventEmitter(); + rowClick: EventEmitter = new EventEmitter(); @Output() - rowDblClick: EventEmitter = new EventEmitter(); + rowDblClick: EventEmitter = new EventEmitter(); + + noContentTemplate: TemplateRef; isSelectAllChecked: boolean = false; + @Output() + showRowContextMenu: EventEmitter = new EventEmitter(); + + @Output() + showRowActionsMenu: EventEmitter = new EventEmitter(); + + @Output() + executeRowAction: EventEmitter = new EventEmitter(); + // TODO: left for reference, will be removed during future revisions constructor(/*private _ngZone?: NgZone*/) { } @@ -84,7 +100,8 @@ export class DataTableComponent implements OnInit, AfterViewChecked { } this.rowClick.emit({ - value: row + value: row, + event: e }); } @@ -94,7 +111,8 @@ export class DataTableComponent implements OnInit, AfterViewChecked { } this.rowDblClick.emit({ - value: row + value: row, + event: e }); } @@ -134,14 +152,16 @@ export class DataTableComponent implements OnInit, AfterViewChecked { isIconValue(row: DataRow, col: DataColumn) { if (row && col) { - return row.getValue(col.key).startsWith('material-icons://'); + let value = row.getValue(col.key); + return value && value.startsWith('material-icons://'); } return false; } asIconValue(row: DataRow, col: DataColumn) { if (this.isIconValue(row, col)) { - return row.getValue(col.key).replace('material-icons://', ''); + let value = row.getValue(col.key) || ''; + return value.replace('material-icons://', ''); } return null; } @@ -153,4 +173,21 @@ export class DataTableComponent implements OnInit, AfterViewChecked { } return false; } + + getContextMenuActions(row: DataRow, col: DataColumn) { + let args = { row: row, col: col, actions: [] }; + this.showRowContextMenu.emit({ args: args }); + return args.actions; + } + + getRowActions(row: DataRow, col: DataColumn) { + let args = { row: row, col: col, actions: [] }; + this.showRowActionsMenu.emit({ args: args }); + return args.actions; + } + + onExecuteRowAction(row: DataRow, action: any) { + let args = { row: row, action: action }; + this.executeRowAction.emit({ args: args }); + } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/models/content-column.model.spec.ts b/ng2-components/ng2-alfresco-datatable/src/components/no-content-template.component.ts similarity index 56% rename from ng2-components/ng2-alfresco-documentlist/src/models/content-column.model.spec.ts rename to ng2-components/ng2-alfresco-datatable/src/components/no-content-template.component.ts index f030113cdc..081d31b733 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/models/content-column.model.spec.ts +++ b/ng2-components/ng2-alfresco-datatable/src/components/no-content-template.component.ts @@ -16,22 +16,26 @@ */ import { - it, - describe, - expect -} from '@angular/core/testing'; + Directive, + ContentChild, + TemplateRef, + AfterContentInit +} from '@angular/core'; +import { DataTableComponent } from './datatable.component'; -import { ContentColumnModel } from './content-column.model'; +@Directive({ + selector: 'no-content-template' +}) +export class NoContentTemplateComponent implements AfterContentInit { -describe('ContentColumnModel', () => { + @ContentChild(TemplateRef) + template: any; - it('should init with text type from config object', () => { - let model = new ContentColumnModel({}); - expect(model.type).toBe(ContentColumnModel.TYPE_TEXT); - }); + constructor( + private dataTable: DataTableComponent) { + } - it('should return supported types', () => { - expect(ContentColumnModel.getSupportedTypes().length).toBeGreaterThan(0); - }); - -}); + ngAfterContentInit() { + this.dataTable.noContentTemplate = this.template; + } +} diff --git a/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts b/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts index cec755921f..41ebaada41 100644 --- a/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts +++ b/ng2-components/ng2-alfresco-datatable/src/data/datatable-adapter.ts @@ -16,7 +16,6 @@ */ export interface DataTableAdapter { - getRows(): Array; setRows(rows: Array): void; getColumns(): Array; @@ -25,33 +24,32 @@ export interface DataTableAdapter { getSorting(): DataSorting; setSorting(sorting: DataSorting): void; sort(key?: string, direction?: string): void; - } export interface DataRow { - isSelected: boolean; hasValue(key: string): boolean; getValue(key: string): any; - } export interface DataColumn { - key: string; - type: string; // text|image + type: string; // text|image|date + format?: string; sortable?: boolean; title?: string; srTitle?: string; cssClass?: string; - } export class DataSorting { - constructor( public key?: string, public direction?: string) { } - +} + +export interface DataRowEvent { + value?: DataRow; + event?: Event; } diff --git a/ng2-components/ng2-alfresco-datatable/src/data/object-datatable-adapter.ts b/ng2-components/ng2-alfresco-datatable/src/data/object-datatable-adapter.ts index 16cf6ad0f7..b5677e0b71 100644 --- a/ng2-components/ng2-alfresco-datatable/src/data/object-datatable-adapter.ts +++ b/ng2-components/ng2-alfresco-datatable/src/data/object-datatable-adapter.ts @@ -15,6 +15,8 @@ * limitations under the License. */ +import { DatePipe } from '@angular/common'; + import { DataTableAdapter, DataRow, @@ -78,7 +80,20 @@ export class ObjectDataTableAdapter implements DataTableAdapter { if (!col) { throw new Error('Column not found'); } - return row.getValue(col.key); + + let value = row.getValue(col.key); + + if (col.type === 'date') { + let datePipe = new DatePipe(); + let format = col.format || 'medium'; + try { + return datePipe.transform(value, format); + } catch (err) { + console.error(`DocumentList: error parsing date ${value} to format ${format}`); + } + } + + return value; } getSorting(): DataSorting { diff --git a/ng2-components/ng2-alfresco-documentlist/demo/package.json b/ng2-components/ng2-alfresco-documentlist/demo/package.json index 30021aa356..3c1407e2a7 100644 --- a/ng2-components/ng2-alfresco-documentlist/demo/package.json +++ b/ng2-components/ng2-alfresco-documentlist/demo/package.json @@ -9,7 +9,7 @@ "typings": "typings install", "postinstall": "npm run typings && npm run build", "start": "concurrently \"npm run build:w\" \"npm run server\" ", - "server": "wsrv -o -s", + "server": "wsrv -o -s -l", "build": "npm run tslint && rimraf dist && tsc", "build:w": "npm run tslint && rimraf dist && tsc -w", "tslint": "npm run tslint-src && npm run tslint-root", @@ -41,7 +41,8 @@ "alfresco-js-api": "^0.1.0", "ng2-alfresco-core": "^0.1.36", - "ng2-alfresco-documentlist": "^0.1.34" + "ng2-alfresco-documentlist": "^0.1.34", + "ng2-alfresco-datatable": "^0.1.17" }, "devDependencies": { "concurrently": "2.0.0", diff --git a/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts b/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts index 0902e6a751..ecdd7eb3b2 100644 --- a/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts +++ b/ng2-components/ng2-alfresco-documentlist/demo/src/main.ts @@ -38,9 +38,9 @@ import { selector: 'alfresco-documentlist-demo', template: `
-
+

-

+

Authentication failed to ip {{ host }} with user: admin, admin, you can still try to add a valid token to perform operations. @@ -52,28 +52,40 @@ import { [currentFolderPath]="currentPath" [target]="documentList"> - - + - + @@ -81,25 +93,16 @@ import { - - @@ -107,37 +110,21 @@ import { - - - - @@ -156,7 +143,8 @@ class DocumentListDemo implements OnInit { currentPath: string = '/'; authenticated: boolean; - public host: string = 'http://devproducts-platform.alfresco.me'; + // host: string = 'http://devproducts-platform.alfresco.me'; + host: string = 'http://127.0.0.1:8080'; token: string; @@ -194,11 +182,13 @@ class DocumentListDemo implements OnInit { } myCustomAction1(event) { - alert('Custom document action for ' + event.value.displayName); + let entry = event.value.entry; + alert(`Custom document action for ${entry.name}`); } myFolderAction1(event) { - alert('Custom folder action for ' + event.value.displayName); + let entry = event.value.entry; + alert(`Custom folder action for ${entry.name}`); } login() { diff --git a/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js b/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js index 13a0e340b1..4831e6d740 100644 --- a/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js +++ b/ng2-components/ng2-alfresco-documentlist/demo/systemjs.config.js @@ -12,6 +12,7 @@ 'ng2-translate': 'node_modules/ng2-translate', 'ng2-alfresco-core': 'node_modules/ng2-alfresco-core/dist', + 'ng2-alfresco-datatable': 'node_modules/ng2-alfresco-datatable/dist', 'ng2-alfresco-documentlist': 'node_modules/ng2-alfresco-documentlist/dist' }; // packages tells the System loader how to load when no filename and/or no extension @@ -22,6 +23,7 @@ 'ng2-translate': { defaultExtension: 'js' }, 'ng2-alfresco-core': { main: 'index.js', defaultExtension: 'js' }, + 'ng2-alfresco-datatable': { main: 'index.js', defaultExtension: 'js' }, 'ng2-alfresco-documentlist': { main: 'index.js', defaultExtension: 'js' } }; var ngPackageNames = [ diff --git a/ng2-components/ng2-alfresco-documentlist/demo/wsrv-config.json b/ng2-components/ng2-alfresco-documentlist/demo/wsrv-config.json new file mode 100644 index 0000000000..3269598468 --- /dev/null +++ b/ng2-components/ng2-alfresco-documentlist/demo/wsrv-config.json @@ -0,0 +1,6 @@ +{ + "watch": [ + "node_modules/ng2-alfresco-datatable/dist/**/*.{html,htm,css,js}", + "node_modules/ng2-alfresco-documentlist/dist/**/*.{html,htm,css,js}" + ] +} diff --git a/ng2-components/ng2-alfresco-documentlist/index.ts b/ng2-components/ng2-alfresco-documentlist/index.ts index da2483d769..946923d327 100644 --- a/ng2-components/ng2-alfresco-documentlist/index.ts +++ b/ng2-components/ng2-alfresco-documentlist/index.ts @@ -25,7 +25,7 @@ import { DocumentListBreadcrumb } from './src/components/document-list-breadcrum import { FolderActionsService } from './src/services/folder-actions.service'; import { DocumentActionsService } from './src/services/document-actions.service'; -import { AlfrescoService } from './src/services/alfresco.service'; +import { DocumentListService } from './src/services/document-list.service'; // components export * from './src/components/document-list'; @@ -36,13 +36,10 @@ export * from './src/components/content-action-list'; export * from './src/components/empty-folder-content'; export * from './src/components/document-list-breadcrumb.component'; -// models -export * from './src/models/column-sorting.model'; - // services export * from './src/services/folder-actions.service'; export * from './src/services/document-actions.service'; -export * from './src/services/alfresco.service'; +export * from './src/services/document-list.service'; export const DOCUMENT_LIST_DIRECTIVES: [any] = [ DocumentList, @@ -55,7 +52,7 @@ export const DOCUMENT_LIST_DIRECTIVES: [any] = [ ]; export const DOCUMENT_LIST_PROVIDERS: [any] = [ - AlfrescoService, + DocumentListService, FolderActionsService, DocumentActionsService ]; diff --git a/ng2-components/ng2-alfresco-documentlist/karma-test-shim.js b/ng2-components/ng2-alfresco-documentlist/karma-test-shim.js index 2d378ec947..bffeaa9753 100644 --- a/ng2-components/ng2-alfresco-documentlist/karma-test-shim.js +++ b/ng2-components/ng2-alfresco-documentlist/karma-test-shim.js @@ -9,15 +9,17 @@ var map = { 'app': 'base/dist', 'rxjs': 'base/node_modules/rxjs', '@angular': 'base/node_modules/@angular', + 'ng2-translate' : '/base/node_modules/ng2-translate', 'ng2-alfresco-core': '/base/node_modules/ng2-alfresco-core/dist', - 'ng2-translate' : '/base/node_modules/ng2-translate' + 'ng2-alfresco-datatable': '/base/node_modules/ng2-alfresco-datatable/dist' }; var packages = { 'app': { main: 'main.js', defaultExtension: 'js' }, 'rxjs': { defaultExtension: 'js' }, + 'ng2-translate': { defaultExtension: 'js' }, 'ng2-alfresco-core': { main: 'index.js', defaultExtension: 'js' }, - 'ng2-translate': { defaultExtension: 'js' } + 'ng2-alfresco-datatable': { main: 'index.js', defaultExtension: 'js' } }; var packageNames = [ diff --git a/ng2-components/ng2-alfresco-documentlist/karma.conf.js b/ng2-components/ng2-alfresco-documentlist/karma.conf.js index c94604b79f..9de1c785c7 100644 --- a/ng2-components/ng2-alfresco-documentlist/karma.conf.js +++ b/ng2-components/ng2-alfresco-documentlist/karma.conf.js @@ -16,6 +16,7 @@ module.exports = function (config) { {pattern: 'node_modules/@angular/**/*.js', included: false, watched: false}, {pattern: 'node_modules/@angular/**/*.map', included: false, watched: false}, {pattern: 'node_modules/ng2-alfresco-core/dist/**/*.js', included: false, served: true, watched: false}, + {pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.js', included: false, served: true, watched: false}, {pattern: 'node_modules/ng2-translate/**/*.js', included: false, served: true, watched: false}, {pattern: 'karma-test-shim.js', included: true, watched: true}, @@ -71,7 +72,7 @@ module.exports = function (config) { // Coverage reporter generates the coverage reporters: ['mocha', 'coverage', 'coveralls', 'kjhtml'], - + // Source files that you wanna generate coverage for. // Do not include tests or libraries (these files will be instrumented by Istanbul) preprocessors: { diff --git a/ng2-components/ng2-alfresco-documentlist/package.json b/ng2-components/ng2-alfresco-documentlist/package.json index e7241ff973..48b5b41e0c 100644 --- a/ng2-components/ng2-alfresco-documentlist/package.json +++ b/ng2-components/ng2-alfresco-documentlist/package.json @@ -28,7 +28,7 @@ "posttest": "node_modules/.bin/remap-istanbul -i coverage/report/coverage-final.json -o coverage/report -t html", "coverage": "npm run test && wsrv -o -p 9875 ./coverage/report", "prepublish": "npm run build", - "travis": "npm link ng2-alfresco-core" + "travis": "npm link ng2-alfresco-core ng2-alfresco-datatable" }, "main": "./dist/index.js", "typings": "./dist/index.d.ts", @@ -75,6 +75,7 @@ "rimraf": "2.5.2", "ng2-translate": "2.2.2", "ng2-alfresco-core": "0.2.0", + "ng2-alfresco-datatable": "0.2.0", "alfresco-js-api": "^0.1.0" }, "peerDependencies": { @@ -103,13 +104,7 @@ }, "license-check-config": { "src": [ - "**/*.js", - "**/*.ts", - "!/**/coverage/**/*", - "!/**/demo/**/*", - "!/**/node_modules/**/*", - "!/**/typings/**/*", - "!*.js" + "./dist/**/*.js" ], "path": "assets/license_header.txt", "blocking": true, diff --git a/ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts b/ng2-components/ng2-alfresco-documentlist/src/assets/document-list.service.mock.ts similarity index 89% rename from ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts rename to ng2-components/ng2-alfresco-documentlist/src/assets/document-list.service.mock.ts index 95ea8be10d..a18b386d2e 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/assets/document-list.service.mock.ts @@ -15,15 +15,15 @@ * limitations under the License. */ -import {Observable} from 'rxjs/Observable'; -import {AlfrescoService} from '../../src/services/alfresco.service'; +import { Observable } from 'rxjs/Observable'; +import { DocumentListService } from './../services/document-list.service'; import { AlfrescoSettingsService, AlfrescoAuthenticationService, AlfrescoContentService } from 'ng2-alfresco-core'; -export class AlfrescoServiceMock extends AlfrescoService { +export class DocumentListServiceMock extends DocumentListService { folderToReturn: any = {}; getFolderReject: boolean = false; diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts index 647e8d0586..b7572ee969 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts @@ -23,7 +23,7 @@ import { } from '@angular/core/testing'; import { DocumentList } from './document-list'; -import { AlfrescoServiceMock } from '../assets/alfresco.service.mock'; +import { DocumentListServiceMock } from '../assets/document-list.service.mock'; import { ContentActionModel } from './../models/content-action.model'; import { ContentActionList } from './content-action-list'; @@ -33,8 +33,8 @@ describe('ContentColumnList', () => { let actionList: ContentActionList; beforeEach(() => { - let alfrescoServiceMock = new AlfrescoServiceMock(); - documentList = new DocumentList(alfrescoServiceMock, null); + let documentListService = new DocumentListServiceMock(); + documentList = new DocumentList(documentListService, null); actionList = new ContentActionList(documentList); }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts index 0f42649e01..59b473841f 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts @@ -24,7 +24,7 @@ import { import { EventEmitter } from '@angular/core'; import { DocumentList } from './document-list'; -import { AlfrescoServiceMock } from '../assets/alfresco.service.mock'; +import { DocumentListServiceMock } from '../assets/document-list.service.mock'; import { ContentActionList } from './content-action-list'; import { ContentAction } from './content-action'; import { DocumentActionsService } from '../services/document-actions.service'; @@ -40,11 +40,11 @@ describe('ContentAction', () => { let folderActions: FolderActionsService; beforeEach(() => { - let alfrescoServiceMock = new AlfrescoServiceMock(); + let documentServiceMock = new DocumentListServiceMock(); documentActions = new DocumentActionsService(null, null); folderActions = new FolderActionsService(null); - documentList = new DocumentList(alfrescoServiceMock, null); + documentList = new DocumentList(documentServiceMock, null); actionList = new ContentActionList(documentList); }); @@ -59,7 +59,6 @@ describe('ContentAction', () => { it('should setup and register model', () => { let action = new ContentAction(actionList, null, null); - action.type = 'button'; action.target = 'document'; action.title = ''; action.icon = '<icon>'; @@ -70,7 +69,6 @@ describe('ContentAction', () => { expect(documentList.actions.length).toBe(1); let model = documentList.actions[0]; - expect(model.type).toBe(action.type); expect(model.target).toBe(action.target); expect(model.title).toBe(action.title); expect(model.icon).toBe(action.icon); @@ -82,7 +80,6 @@ describe('ContentAction', () => { spyOn(documentActions, 'getHandler').and.returnValue(handler); let action = new ContentAction(actionList, documentActions, null); - action.type = 'button'; action.target = 'document'; action.handler = '<handler>'; action.ngOnInit(); @@ -99,7 +96,6 @@ describe('ContentAction', () => { spyOn(folderActions, 'getHandler').and.returnValue(handler); let action = new ContentAction(actionList, null, folderActions); - action.type = 'button'; action.target = 'folder'; action.handler = '<handler>'; action.ngOnInit(); @@ -116,7 +112,6 @@ describe('ContentAction', () => { spyOn(documentActions, 'getHandler').and.stub(); let action = new ContentAction(actionList, documentActions, folderActions); - action.type = 'button'; action.handler = '<handler>'; action.ngOnInit(); @@ -138,7 +133,6 @@ describe('ContentAction', () => { let action = new ContentAction(actionList, documentActions, null); action.target = 'DoCuMeNt'; - action.type = 'button'; action.handler = '<handler>'; action.ngOnInit(); @@ -150,7 +144,6 @@ describe('ContentAction', () => { let action = new ContentAction(actionList, null, folderActions); action.target = 'FoLdEr'; - action.type = 'button'; action.handler = '<handler>'; action.ngOnInit(); @@ -167,7 +160,6 @@ describe('ContentAction', () => { let action = new ContentAction(actionList, null, null); action.target = 'document'; - action.type = 'button'; action.execute = emitter; action.ngOnInit(); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts index 2c0f2859a6..03ad0740c6 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts @@ -15,12 +15,20 @@ * limitations under the License. */ -import {Component, OnInit, OnChanges, Input, Output, EventEmitter} from '@angular/core'; -import {ContentActionModel} from './../models/content-action.model'; -import {ContentActionList} from './content-action-list'; -import {DocumentActionsService} from '../services/document-actions.service'; -import {FolderActionsService} from '../services/folder-actions.service'; -import {ContentActionHandler} from '../models/content-action.model'; +import { + Component, + OnInit, + OnChanges, + Input, + Output, + EventEmitter +} from '@angular/core'; + +import { ContentActionModel } from './../models/content-action.model'; +import { ContentActionList } from './content-action-list'; +import { DocumentActionsService } from '../services/document-actions.service'; +import { FolderActionsService } from '../services/folder-actions.service'; +import { ContentActionHandler } from '../models/content-action.model'; @Component({ selector: 'content-action', @@ -37,9 +45,6 @@ export class ContentAction implements OnInit, OnChanges { @Input() handler: string; - @Input() - type: string; - @Input() target: string; @@ -57,7 +62,6 @@ export class ContentAction implements OnInit, OnChanges { ngOnInit() { this.model = new ContentActionModel({ - type: this.type, title: this.title, icon: this.icon, target: this.target diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts index 0d6b52e8e5..084f7c0ca3 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts @@ -22,10 +22,11 @@ import { beforeEach } from '@angular/core/testing'; -import {DocumentList} from './document-list'; -import {AlfrescoServiceMock} from '../assets/alfresco.service.mock'; -import {ContentColumnList} from './content-column-list'; -import {ContentColumnModel} from '../models/content-column.model'; +import { DataColumn } from 'ng2-alfresco-datatable'; + +import { DocumentList } from './document-list'; +import { DocumentListServiceMock } from '../assets/document-list.service.mock'; +import { ContentColumnList } from './content-column-list'; describe('ContentColumnList', () => { @@ -33,23 +34,26 @@ describe('ContentColumnList', () => { let columnList: ContentColumnList; beforeEach(() => { - let alfrescoServiceMock = new AlfrescoServiceMock(); - documentList = new DocumentList(alfrescoServiceMock, null); + let service = new DocumentListServiceMock(); + documentList = new DocumentList(service, null); columnList = new ContentColumnList(documentList); }); it('should register column within parent document list', () => { - expect(documentList.columns.length).toBe(0); + let columns = documentList.data.getColumns(); + expect(columns.length).toBe(0); - let result = columnList.registerColumn(new ContentColumnModel()); + let column = <DataColumn> {}; + let result = columnList.registerColumn(column); expect(result).toBeTruthy(); - expect(documentList.columns.length).toBe(1); + expect(columns.length).toBe(1); + expect(columns[0]).toBe(column); }); it('should require document list instance to register action', () => { columnList = new ContentColumnList(null); - let col = new ContentColumnModel(); + let col = <DataColumn> {}; expect(columnList.registerColumn(col)).toBeFalsy(); }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts index b6b126227f..f84c158e8e 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import {Component} from '@angular/core'; -import {DocumentList} from './document-list'; -import {ContentColumnModel} from './../models/content-column.model'; +import { Component } from '@angular/core'; +import { DocumentList } from './document-list'; +import { DataColumn } from 'ng2-alfresco-datatable'; @Component({ selector: 'content-columns', @@ -33,9 +33,10 @@ export class ContentColumnList { * Registers column model within the parent document list component. * @param column Column definition model to register. */ - registerColumn(column: ContentColumnModel): boolean { + registerColumn(column: DataColumn): boolean { if (this.documentList && column) { - this.documentList.columns.push(column); + let columns = this.documentList.data.getColumns(); + columns.push(column); return true; } return false; diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts index f44502c9f8..ab9d4dab78 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts @@ -22,10 +22,10 @@ import { beforeEach } from '@angular/core/testing'; -import {DocumentList} from './document-list'; -import {ContentColumn} from './content-column'; -import {AlfrescoServiceMock} from '../assets/alfresco.service.mock'; -import {ContentColumnList} from './content-column-list'; +import { DocumentList } from './document-list'; +import { ContentColumn } from './content-column'; +import { DocumentListServiceMock } from '../assets/document-list.service.mock'; +import { ContentColumnList } from './content-column-list'; describe('ContentColumn', () => { @@ -33,8 +33,8 @@ describe('ContentColumn', () => { let columnList: ContentColumnList; beforeEach(() => { - let alfrescoServiceMock = new AlfrescoServiceMock(); - documentList = new DocumentList(alfrescoServiceMock, null); + let service = new DocumentListServiceMock(); + documentList = new DocumentList(service, null); columnList = new ContentColumnList(documentList); }); @@ -45,52 +45,18 @@ describe('ContentColumn', () => { column.ngOnInit(); expect(columnList.registerColumn).toHaveBeenCalled(); - }); - it('should setup model properties during registration', () => { - - let column = new ContentColumn(columnList); - column.title = '<title>'; - column.srTitle = '<sr-title>'; - column.source = '<source>'; - column.cssClass = '<css-class>'; - column.ngOnInit(); - - expect(documentList.columns.length).toBe(1); - - let model = documentList.columns[0]; - expect(model.title).toBe(column.title); - expect(model.srTitle).toBe(column.srTitle); - expect(model.source).toBe(column.source); - expect(model.cssClass).toBe(column.cssClass); + let columns = documentList.data.getColumns(); + expect(columns.length).toBe(1); + expect(columns[0]).toBe(column); }); it('should setup screen reader title for thumbnail column', () => { - let column = new ContentColumn(columnList); - column.source = '$thumbnail'; + column.key = '$thumbnail'; column.ngOnInit(); - expect(documentList.columns.length).toBe(1); - - let model = documentList.columns[0]; - expect(model.srTitle).toBe('Thumbnail'); - }); - - it('should sync localizable fields with model', () => { - - let column = new ContentColumn(columnList); - column.title = 'title1'; - column.srTitle = 'srTitle1'; - column.ngOnInit(); - - expect(column.model.title).toBe(column.title); - expect(column.model.srTitle).toBe(column.srTitle); - - column.title = 'title2'; - column.ngOnChanges(null); - - expect(column.model.title).toBe('title2'); + expect(column.srTitle).toBe('Thumbnail'); }); it('should register on init', () => { diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts index 3b0c9d40ff..3f5f715f6d 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts @@ -15,15 +15,27 @@ * limitations under the License. */ -import { Component, OnInit, Input, OnChanges } from '@angular/core'; +import { Component, OnInit, Input } from '@angular/core'; import { ContentColumnList } from './content-column-list'; -import { ContentColumnModel } from './../models/content-column.model'; +import { DataColumn } from 'ng2-alfresco-datatable'; @Component({ selector: 'content-column', template: '' }) -export class ContentColumn implements OnInit, OnChanges { +export class ContentColumn implements OnInit, DataColumn { + + @Input() + key: string; + + @Input() + type: string = 'text'; + + @Input() + format: string; + + @Input() + sortable: boolean = false; @Input() title: string = ''; @@ -34,36 +46,14 @@ export class ContentColumn implements OnInit, OnChanges { @Input('sr-title') srTitle: string; - @Input() - source: string; - @Input('class') cssClass: string; - @Input() - type: string = 'text'; - - @Input() - format: string; - - model: ContentColumnModel; - - constructor(private list: ContentColumnList) { - this.model = new ContentColumnModel(); - } + constructor(private list: ContentColumnList) {} ngOnInit() { - this.model = new ContentColumnModel({ - title: this.title, - srTitle: this.srTitle, - source: this.source, - cssClass: this.cssClass, - type: this.type, - format: this.format - }); - - if (!this.model.srTitle && this.model.source === '$thumbnail') { - this.model.srTitle = 'Thumbnail'; + if (!this.srTitle && this.key === '$thumbnail') { + this.srTitle = 'Thumbnail'; } this.register(); @@ -71,14 +61,8 @@ export class ContentColumn implements OnInit, OnChanges { register(): boolean { if (this.list) { - return this.list.registerColumn(this.model); + return this.list.registerColumn(this); } return false; } - - ngOnChanges(change) { - // update localizable properties - this.model.title = this.title; - this.model.srTitle = this.srTitle; - } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.css b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.css index eab03ea4c8..e69de29bb2 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.css +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.css @@ -1,105 +0,0 @@ -:host .full-width { width: 100%; } - -:host .thumbnail { - width: 48px; - height: 48px; - cursor: default; -} - -:host .column-header { - cursor: pointer; - user-select: none; - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE/Edge */ - -webkit-touch-callout: none; /* iOS Safari */ -} - -:host .parent-folder-link { cursor: default; } -:host .parent-folder-link > td { text-align: left; } - -:host .data-cell { - cursor: default; -} -:host .cell-value {} - -/* Empty folder */ - -:host .empty-folder-content { - padding: 0 !important; -} - -:host .empty-folder-content > img { - width: 100%; -} - -/* Utils */ - -:host .non-selectable { - user-select: none; - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE/Edge */ - -webkit-touch-callout: none; /* iOS Safari */ -} - -:host .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; -} - - -:host .ellipsis-cell > div -{ - position: relative; - overflow: hidden; - height: 1em; -} - - -/* visible content */ -:host .ellipsis-cell > div > span -{ - display: block; - position: absolute; - max-width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - line-height: 1em; /* for vertical align of text */ -} - - -/* cell stretching content */ -:host .ellipsis-cell > div:after -{ - content: attr(title); - overflow: hidden; - height: 0; - display: block; -} - -/* small desktop */ -@media all and (max-width: 1200px) {} - -/* tablet */ -@media all and (max-width: 1024px) {} - -/* mobile phone */ -@media all and (max-width: 768px) { - .desktop-only { - display: none; - } -} - -@media (max-device-width: 768px){ - .desktop-only { - display: none; - } -} diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html index a3cc0693ed..5964792515 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.html @@ -1,103 +1,15 @@ -<table *ngIf="folder" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width"> - <thead> - <tr> - <!-- Columns --> - <th class="mdl-data-table__cell--non-numeric non-selectable {{col.cssClass}}" - *ngFor="let col of columns" - [class.column-header]="col.title" - [attr.data-automation-id]="'auto_id_' + col.source" - [class.mdl-data-table__header--sorted-ascending]="sorting.key === col.source && sorting.direction === 'asc'" - [class.mdl-data-table__header--sorted-descending]="sorting.key === col.source && sorting.direction === 'desc'" - (click)="onColumnHeaderClick(col)"> - <span *ngIf="col.srTitle" class="cell-value sr-only">{{col.srTitle}}</span> - <span *ngIf="col.title" class="cell-value">{{col.title}}</span> - </th> - <!-- Actions --> - <th> - <span class="sr-only">Actions</span> - </th> - </tr> - </thead> - <tbody> - <tr *ngFor="let content of folder.list.entries; let idx = index" - [attr.data-automation-id]="getObjectValue(content.entry, 'name')"> - <!-- Columns --> - <td *ngFor="let col of columns" [ngSwitch]="col.type" - class="mdl-data-table__cell--non-numeric non-selectable data-cell {{col.cssClass}}" - (click)="onItemClick(content, $event)" - (dblclick)="onItemDblClick(content, $event)" - [context-menu]="getContextActions(content)" - [attr.data-automation-id]="col.source === '$thumbnail' ? '$thumbnail' : col.source + '_' + getObjectValue(content.entry, col.source)"> - <div *ngSwitchCase="'image'" class="cell-value"> - <img class="thumbnail" [src]="getCellValue(content, col)"> - </div> - <div *ngSwitchCase="'date'" class="cell-value"> - <span>{{ getCellValue(content, col) }}</span> - </div> - <div *ngSwitchDefault class="cell-value"> - <span>{{ getCellValue(content, col) }}</span> - </div> - </td> - - <!-- Actions: folder --> - <td *ngIf="content.entry.isFolder"> - <!-- action buttons --> - <button class="mdl-button mdl-js-button mdl-button--icon" - *ngFor="let action of getContentActions('folder', 'button')" - (click)="executeContentAction(content, action)"> - <i class="material-icons">{{action.icon}}</i> - </button> - - <!-- action menu --> - <button [id]="'folder_action_menu_' + idx" class="mdl-button mdl-js-button mdl-button--icon"> - <i class="material-icons">more_vert</i> - </button> - <ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" - [attr.for]="'folder_action_menu_' + idx"> - <li class="mdl-menu__item" - [attr.data-automation-id]="action.title" - *ngFor="let action of getContentActions('folder', 'menu')" - (click)="executeContentAction(content, action)"> - {{action.title}} - </li> - </ul> - </td> - <!-- Actions: document --> - <td *ngIf="!content.entry.isFolder"> - <!-- action buttons --> - <button class="mdl-button mdl-js-button mdl-button--icon" - *ngFor="let action of getContentActions('document', 'button')" - (click)="executeContentAction(content, action)"> - <i class="material-icons">{{action.icon}}</i> - </button> - - <!-- action menu --> - <button [id]="'document_action_menu_' + idx" class="mdl-button mdl-js-button mdl-button--icon"> - <i class="material-icons">more_vert</i> - </button> - <ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" - [attr.for]="'document_action_menu_' + idx"> - <li class="mdl-menu__item" - [attr.data-automation-id]="action.title" - *ngFor="let action of getContentActions('document', 'menu')" - (click)="executeContentAction(content, action)"> - {{action.title}} - </li> - </ul> - </td> - </tr> - - <tr *ngIf="folder?.list?.entries?.length === 0"> - <td class="mdl-data-table__cell--non-numeric empty-folder-content" - [attr.colspan]="1 + columns?.length"> - <template *ngIf="emptyFolderTemplate" - ngFor [ngForOf]="[folder]" - [ngForTemplate]="emptyFolderTemplate"> - </template> - <img *ngIf="!emptyFolderTemplate" - [src]="baseComponentPath + '/img/document-list.empty-folder.png'"> - </td> - </tr> - - </tbody> -</table> +<alfresco-datatable + [data]="data" + [actions]="contentActions" + [multiselect]="multiselect" + (showRowContextMenu)="onShowRowContextMenu($event)" + (showRowActionsMenu)="onShowRowActionsMenu($event)" + (executeRowAction)="onExecuteRowAction($event)" + (rowClick)="onRowClick($event)" + (rowDblClick)="onRowDblClick($event)"> + <no-content-template> + <template> + <img [src]="baseComponentPath + '/img/document-list.empty-folder.png'"> + </template> + </no-content-template> +</alfresco-datatable> diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts index 67b9d6cc42..c9e373536b 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts @@ -15,35 +15,26 @@ * limitations under the License. */ -import { - it, - describe, - expect, - beforeEach -} from '@angular/core/testing'; +import { it, describe, expect, beforeEach } from '@angular/core/testing'; import { NgZone } from '@angular/core'; +import { DataColumn } from 'ng2-alfresco-datatable'; import { DocumentList } from './document-list'; -import { ContentColumnModel } from '../models/content-column.model'; -import { AlfrescoServiceMock } from '../assets/alfresco.service.mock'; +import { DocumentListServiceMock } from './../assets/document-list.service.mock'; import { ContentActionModel } from '../models/content-action.model'; -import { - PageNode, - FileNode, - FolderNode -} from '../assets/document-library.model.mock'; -import { ColumnSortingModel } from '../models/column-sorting.model'; +import { FileNode, FolderNode } from '../assets/document-library.model.mock'; +import { MinimalNodeEntity } from '../models/document-library.model'; describe('DocumentList', () => { - let alfrescoServiceMock: AlfrescoServiceMock; + let documentListService: DocumentListServiceMock; let documentList: DocumentList; let eventMock: any; let componentHandler; beforeEach(() => { - alfrescoServiceMock = new AlfrescoServiceMock(); + documentListService = new DocumentListServiceMock(); let zone = new NgZone(false); - documentList = new DocumentList(alfrescoServiceMock, zone); + documentList = new DocumentList(documentListService, zone); eventMock = { preventDefault: function () { @@ -63,56 +54,67 @@ describe('DocumentList', () => { documentList.ngAfterContentInit(); expect(documentList.setupDefaultColumns).toHaveBeenCalled(); - expect(documentList.columns.length).not.toBe(0); + expect(documentList.data.getColumns().length).not.toBe(0); }); it('should use custom columns instead of default ones', () => { - let column: ContentColumnModel = { + let column = <DataColumn> { title: 'title', - source: 'source', + key: 'source', cssClass: 'css', srTitle: '', type: 'text', format: '' }; - documentList.columns.push(column); + + let columns = documentList.data.getColumns(); + columns.push(column); documentList.ngAfterContentInit(); - expect(documentList.columns.length).toBe(1); - expect(documentList.columns[0]).toBe(column); + expect(columns.length).toBe(1); + expect(columns[0]).toBe(column); }); + // TODO: move to data adapter + /* it('should fetch folder', () => { let folder = { 'nodeRef': 'workspace://SpacesStore/8bb36efb-c26d-4d2b-9199-ab6922f53c28' }; - alfrescoServiceMock.folderToReturn = folder; + documentListService.folderToReturn = folder; documentList.ngOnInit(); expect(documentList.folder).toBe(folder); }); + */ + // TODO: move to data adapter + /* it('should return thumbnail url for a file when thumbnails turned on', () => { let url = 'URL'; - spyOn(alfrescoServiceMock, 'getDocumentThumbnailUrl').and.returnValue(url); + spyOn(documentListService, 'getDocumentThumbnailUrl').and.returnValue(url); let node = new FileNode(); documentList.thumbnails = true; let result = documentList.getThumbnailUrl(node); expect(result).toBe(url); - expect(alfrescoServiceMock.getDocumentThumbnailUrl).toHaveBeenCalled(); + expect(documentListService.getDocumentThumbnailUrl).toHaveBeenCalled(); }); + */ + // TODO: move to data adapter + /* it('should return a null thumbnail url for a null item', () => { let url = 'URL'; - spyOn(alfrescoServiceMock, 'getDocumentThumbnailUrl').and.returnValue(url); + spyOn(documentListService, 'getDocumentThumbnailUrl').and.returnValue(url); let result = documentList.getThumbnailUrl(null); expect(result).toBeNull(); - expect(alfrescoServiceMock.getDocumentThumbnailUrl).not.toHaveBeenCalled(); + expect(documentListService.getDocumentThumbnailUrl).not.toHaveBeenCalled(); }); + */ it('should execute action with node', () => { let node = new FileNode(); @@ -139,100 +141,54 @@ describe('DocumentList', () => { expect(action.handler).not.toHaveBeenCalled(); }); - it('should give no content actions for empty target', () => { - let actions = documentList.getContentActions(null, 'button'); + it('should not give node actions for empty target', () => { + let actions = documentList.getNodeActions(null); expect(actions.length).toBe(0); }); - it('should give no content actions for empty type', () => { - let actions = documentList.getContentActions('folder', null); - expect(actions.length).toBe(0); - }); - - it('should filter content actions for various types and targets', () => { - let folderButton = new ContentActionModel(); - folderButton.target = 'folder'; - folderButton.type = 'button'; - + it('should filter content actions for various targets', () => { let folderMenu = new ContentActionModel(); folderMenu.target = 'folder'; - folderMenu.type = 'menu'; - - let documentButton = new ContentActionModel(); - documentButton.target = 'document'; - documentButton.type = 'button'; let documentMenu = new ContentActionModel(); documentMenu.target = 'document'; - documentMenu.type = 'menu'; documentList.actions = [ - folderButton, folderMenu, - documentButton, documentMenu ]; - let actions = documentList.getContentActions('folder', 'button'); - expect(actions.length).toBe(1); - expect(actions[0]).toBe(folderButton); - actions = documentList.getContentActions('folder', 'menu'); + let actions = documentList.getNodeActions(new FolderNode()); expect(actions.length).toBe(1); expect(actions[0]).toBe(folderMenu); - actions = documentList.getContentActions('document', 'button'); - expect(actions.length).toBe(1); - expect(actions[0]).toBe(documentButton); - - actions = documentList.getContentActions('document', 'menu'); + actions = documentList.getNodeActions(new FileNode()); expect(actions.length).toBe(1); expect(actions[0]).toBe(documentMenu); }); - it('should be case insensitive when filtering content actions', () => { - let documentButton = new ContentActionModel(); - documentButton.target = 'document'; - documentButton.type = 'button'; - - documentList.actions = [documentButton]; - - let actions = documentList.getContentActions('DoCuMeNt', 'BUTTON'); - expect(actions.length).toBe(1); - expect(actions[0]).toBe(documentButton); - }); - it('should find no content actions', () => { let documentButton = new ContentActionModel(); documentButton.target = 'document'; - documentButton.type = 'button'; - documentList.actions = [documentButton]; - let actions = documentList.getContentActions('unknown', 'value'); - expect(actions.length).toBe(0); + let node = new MinimalNodeEntity(); + expect(documentList.getNodeActions(node)).toEqual([]); + + node = new FileNode(); + node.entry.isFile = false; + node.entry.isFolder = false; + expect(documentList.getNodeActions(node)).toEqual([]); }); - it('should emit itemClick event', (done) => { + it('should emit nodeClick event', (done) => { let node = new FileNode(); - documentList.itemClick.subscribe(e => { + documentList.nodeClick.subscribe(e => { expect(e.value).toBe(node); done(); }); - documentList.onItemClick(node); - }); - - it('should prevent default item single click event', () => { - spyOn(eventMock, 'preventDefault').and.stub(); - - documentList.onItemClick(null, eventMock); - expect(eventMock.preventDefault).toHaveBeenCalled(); - }); - - it('should prevent default item double click event', () => { - spyOn(eventMock, 'preventDefault').and.stub(); - documentList.onItemDblClick(null, eventMock); - expect(eventMock.preventDefault).toHaveBeenCalled(); + documentList.onNodeClick(node); }); it('should display folder content on click', () => { @@ -244,7 +200,7 @@ describe('DocumentList', () => { spyOn(documentList, 'displayFolderContent').and.stub(); documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION; - documentList.onItemClick(node); + documentList.onNodeClick(node); expect(documentList.currentFolderPath).toBe(path); }); @@ -253,7 +209,7 @@ describe('DocumentList', () => { expect(documentList.navigate).toBe(true); spyOn(documentList, 'displayFolderContent').and.stub(); - documentList.onItemClick(null); + documentList.onNodeClick(null); expect(documentList.displayFolderContent).not.toHaveBeenCalled(); }); @@ -263,7 +219,7 @@ describe('DocumentList', () => { spyOn(documentList, 'displayFolderContent').and.stub(); let node = new FileNode(); - documentList.onItemClick(node); + documentList.onNodeClick(node); expect(documentList.displayFolderContent).not.toHaveBeenCalled(); }); @@ -273,7 +229,7 @@ describe('DocumentList', () => { let node = new FolderNode('<display name>'); documentList.navigate = false; - documentList.onItemClick(node); + documentList.onNodeClick(node); expect(documentList.displayFolderContent).not.toHaveBeenCalled(); }); @@ -282,33 +238,6 @@ describe('DocumentList', () => { expect(documentList.getNodePath(null)).toBe(null); }); - it('should return root object value', () => { - let target = { - key1: 'value1' - }; - - expect(documentList.getObjectValue(target, 'key1')).toBe('value1'); - }); - - it('should return no object value when key is missing', () => { - let target = { - key1: 'value1' - }; - expect(documentList.getObjectValue(target, 'missing')).toBeUndefined(); - }); - - it('should return nested object value', () => { - let target = { - key1: { - key2: { - key3: 'value1' - } - } - }; - - expect(documentList.getObjectValue(target, 'key1.key2.key3')).toBe('value1'); - }); - it('should display folder content for new folder path', () => { spyOn(documentList, 'displayFolderContent').and.stub(); let newPath = '/some/new/path'; @@ -334,42 +263,44 @@ describe('DocumentList', () => { }); it('should emit folder changed event', (done) => { + spyOn(documentList, 'displayFolderContent').and.stub(); documentList.folderChange.subscribe(e => { done(); }); - documentList.folder = new PageNode(); + + documentList.currentFolderPath = '/some/new/path'; }); it('should emit folder changed event with folder details', (done) => { - let folder = new PageNode(); + spyOn(documentList, 'displayFolderContent').and.stub(); + let path = '/path'; documentList.folderChange.subscribe(e => { - expect(e.value).toBe(folder); expect(e.path).toBe(path); done(); }); - spyOn(documentList, 'displayFolderContent').and.stub(); documentList.currentFolderPath = path; - documentList.folder = folder; }); - it('should not emit folder changed event', () => { - let folder = new PageNode(); + it('should emit folder changed event only once', () => { + spyOn(documentList, 'displayFolderContent').and.stub(); + let path = '/new/path'; let calls = 0; documentList.folderChange.subscribe(e => { calls++; }); - documentList.folder = folder; - documentList.folder = folder; + documentList.currentFolderPath = path; + documentList.currentFolderPath = path; + documentList.currentFolderPath = path; expect(calls).toBe(1); }); it('should reload on binding changes', () => { spyOn(documentList, 'reload').and.stub(); - documentList.ngOnChanges(null); + documentList.ngOnChanges(); expect(documentList.reload).toHaveBeenCalled(); }); @@ -396,8 +327,9 @@ describe('DocumentList', () => { }); it('should subscribe to context action handler', () => { - let value = {}; + spyOn(documentList, 'displayFolderContent').and.stub(); spyOn(documentList, 'contextActionCallback').and.stub(); + let value = {}; documentList.ngOnInit(); documentList.contextActionHandler.next(value); expect(documentList.contextActionCallback).toHaveBeenCalledWith(value); @@ -416,7 +348,7 @@ describe('DocumentList', () => { done(); }); documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION; - documentList.onItemClick(file, null); + documentList.onNodeClick(file); }); it('should emit file preview event on double click', (done) => { @@ -426,7 +358,7 @@ describe('DocumentList', () => { done(); }); documentList.navigationMode = DocumentList.DOUBLE_CLICK_NAVIGATION; - documentList.onItemDblClick(file, null); + documentList.onNodeDblClick(file); }); it('should perform folder navigation on single click', () => { @@ -434,7 +366,7 @@ describe('DocumentList', () => { spyOn(documentList, 'performNavigation').and.stub(); documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION; - documentList.onItemClick(folder, null); + documentList.onNodeClick(folder); expect(documentList.performNavigation).toHaveBeenCalled(); }); @@ -443,7 +375,7 @@ describe('DocumentList', () => { spyOn(documentList, 'performNavigation').and.stub(); documentList.navigationMode = DocumentList.DOUBLE_CLICK_NAVIGATION; - documentList.onItemDblClick(folder, null); + documentList.onNodeDblClick(folder); expect(documentList.performNavigation).toHaveBeenCalled(); }); @@ -452,7 +384,7 @@ describe('DocumentList', () => { spyOn(documentList, 'performNavigation').and.stub(); documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION; - documentList.onItemDblClick(folder, null); + documentList.onNodeDblClick(folder); expect(documentList.performNavigation).not.toHaveBeenCalled(); }); @@ -463,15 +395,16 @@ describe('DocumentList', () => { documentList.navigate = false; documentList.navigationMode = DocumentList.DOUBLE_CLICK_NAVIGATION; - documentList.onItemDblClick(folder, null); + documentList.onNodeDblClick(folder); expect(documentList.performNavigation).not.toHaveBeenCalled(); }); it('should perform navigation for folder node only', () => { + spyOn(documentList, 'getNodePath').and.returnValue('/path'); + let folder = new FolderNode(); let file = new FileNode(); - spyOn(documentList, 'getNodePath').and.returnValue('/path'); expect(documentList.performNavigation(folder)).toBeTruthy(); expect(documentList.performNavigation(file)).toBeFalsy(); @@ -488,7 +421,6 @@ describe('DocumentList', () => { expect(documentList.getNodePath(file)).toBe('/folder1/file.txt'); }); - it('should require valid node for file preview', () => { let file = new FileNode(); file.entry = null; @@ -497,11 +429,11 @@ describe('DocumentList', () => { documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION; documentList.preview.subscribe(val => called = true); - documentList.onItemClick(file, null); + documentList.onNodeClick(file); expect(called).toBeFalsy(); documentList.navigationMode = DocumentList.DOUBLE_CLICK_NAVIGATION; - documentList.onItemDblClick(file, null); + documentList.onNodeDblClick(file); expect(called).toBeFalsy(); }); @@ -511,10 +443,10 @@ describe('DocumentList', () => { spyOn(documentList, 'performNavigation').and.stub(); documentList.navigationMode = DocumentList.SINGLE_CLICK_NAVIGATION; - documentList.onItemClick(folder, null); + documentList.onNodeClick(folder); documentList.navigationMode = DocumentList.DOUBLE_CLICK_NAVIGATION; - documentList.onItemDblClick(folder, null); + documentList.onNodeDblClick(folder); expect(documentList.performNavigation).not.toHaveBeenCalled(); }); @@ -525,6 +457,8 @@ describe('DocumentList', () => { expect(documentList.displayFolderContent).toHaveBeenCalled(); }); + // TODO: move to data adapter + /* it('should generate thumbnail for unknown content', () => { documentList.baseComponentPath = '/root'; let node = new FileNode(); @@ -532,13 +466,19 @@ describe('DocumentList', () => { expect(documentList.getThumbnailUrl(node)).toBe('/root/img/ft_ic_miscellaneous.svg'); }); + */ + // TODO: move to data adapter + /* it('should generate folder icon path', () => { documentList.baseComponentPath = '/root'; let folder = new FolderNode(); expect(documentList.getThumbnailUrl(folder)).toBe('/root/img/ft_ic_folder.svg'); }); + */ + // TODO: move to data adapter + /* it('should generate file icon path based on mime type', () => { let fileName = 'custom-icon.svg'; spyOn(alfrescoServiceMock, 'getMimeTypeIcon').and.returnValue(fileName); @@ -549,9 +489,12 @@ describe('DocumentList', () => { expect(documentList.getThumbnailUrl(file)).toBe(`/root/img/${fileName}`); }); + */ + // TODO: move to data adapter + /* it('should fallback to default icon for missing mime type', () => { - spyOn(alfrescoServiceMock, 'getMimeTypeIcon').and.returnValue(null); + spyOn(documentListService, 'getMimeTypeIcon').and.returnValue(null); documentList.baseComponentPath = '/root'; let file = new FileNode(); @@ -559,9 +502,12 @@ describe('DocumentList', () => { expect(documentList.getThumbnailUrl(file)).toBe('/root/img/ft_ic_miscellaneous.svg'); }); + */ + // TODO: move to data adapter + /* it('should fallback to default icon for unknown mime type', () => { - spyOn(alfrescoServiceMock, 'getMimeTypeIcon').and.returnValue(null); + spyOn(documentListService, 'getMimeTypeIcon').and.returnValue(null); documentList.baseComponentPath = '/root'; let file = new FileNode(); @@ -569,17 +515,23 @@ describe('DocumentList', () => { expect(documentList.getThumbnailUrl(file)).toBe('/root/img/ft_ic_miscellaneous.svg'); }); + */ + // TODO: move to data adapter + /* it('should resolve thumbnail url for a file', () => { let url = 'http://<some url>'; - spyOn(alfrescoServiceMock, 'getDocumentThumbnailUrl').and.returnValue(url); + spyOn(documentListService, 'getDocumentThumbnailUrl').and.returnValue(url); documentList.thumbnails = true; let file = new FileNode(); expect(documentList.getThumbnailUrl(file)).toBe(url); }); + */ + // TODO: move to data adapter + /* it('should return no thumbnail url with missing service', () => { let list = new DocumentList(null, null); list.thumbnails = true; @@ -587,76 +539,10 @@ describe('DocumentList', () => { let file = new FileNode(); expect(list.getThumbnailUrl(file)).toBeNull(); }); + */ - it('should sort on column header click', () => { - let col = new ContentColumnModel(); - col.source = 'id'; - - spyOn(documentList, 'sort').and.callThrough(); - - documentList.onColumnHeaderClick(col); - - expect(documentList.sorting).toEqual( - jasmine.objectContaining({ - key: 'id', - direction: 'asc' - }) - ); - expect(documentList.sort).toHaveBeenCalled(); - }); - - it('should invert sorting on column header click', () => { - let col = new ContentColumnModel(); - col.source = 'id'; - - spyOn(documentList, 'sort').and.callThrough(); - - documentList.sorting = <ColumnSortingModel> { key: 'id', direction: 'asc' }; - documentList.onColumnHeaderClick(col); - - expect(documentList.sorting).toEqual( - jasmine.objectContaining({ - key: 'id', - direction: 'desc' - }) - ); - - documentList.onColumnHeaderClick(col); - expect(documentList.sorting).toEqual( - jasmine.objectContaining({ - key: 'id', - direction: 'asc' - }) - ); - - expect(documentList.sort).toHaveBeenCalledTimes(2); - }); - - it('should use ascending direction for different column header click', () => { - let col = new ContentColumnModel(); - col.source = 'id'; - - spyOn(documentList, 'sort').and.callThrough(); - - documentList.sorting = <ColumnSortingModel> { key: 'col1', direction: 'desc' }; - documentList.onColumnHeaderClick(col); - - expect(documentList.sorting).toEqual( - jasmine.objectContaining({ - key: 'id', - direction: 'asc' - }) - ); - - expect(documentList.sort).toHaveBeenCalled(); - }); - - it('should not sort by column header when instance is missing', () => { - spyOn(documentList, 'sort').and.callThrough(); - documentList.onColumnHeaderClick(null); - expect(documentList.sort).not.toHaveBeenCalled(); - }); - + // TODO: move to DataTable + /* it('should convert cell value to formatted date', () => { let rawValue = new Date(2015, 6, 15, 21, 43, 11).toString(); // Wed Jul 15 2015 21:43:11 GMT+0100 (BST); @@ -673,7 +559,10 @@ describe('DocumentList', () => { let value = documentList.getCellValue(file, col); expect(value).toBe(dateValue); }); + */ + // TODO: move to DataTable + /* it('should return date value as string', () => { let rawValue = new Date(2015, 6, 15, 21, 43, 11).toString(); // Wed Jul 15 2015 21:43:11 GMT+0100 (BST); @@ -687,7 +576,10 @@ describe('DocumentList', () => { let value = documentList.getCellValue(file, col); expect(value).toBe(rawValue); }); + */ + // TODO: move to data adapter + /* it('should convert cell value to thumbnail', () => { let url = 'http://<address>'; spyOn(documentList, 'getThumbnailUrl').and.returnValue(url); @@ -701,14 +593,15 @@ describe('DocumentList', () => { let value = documentList.getCellValue(file, col); expect(value).toBe(url); }); + */ it('should require path to display folder content', () => { - spyOn(alfrescoServiceMock, 'getFolder').and.callThrough(); + spyOn(documentListService, 'getFolder').and.callThrough(); documentList.displayFolderContent(null); documentList.displayFolderContent(''); - expect(alfrescoServiceMock.getFolder).not.toHaveBeenCalled(); + expect(documentListService.getFolder).not.toHaveBeenCalled(); }); it('should require node to resolve context menu actions', () => { @@ -722,12 +615,12 @@ describe('DocumentList', () => { it('should fetch context menu actions for a file node', () => { let actionModel = {}; - spyOn(documentList, 'getContentActions').and.returnValue([actionModel]); + spyOn(documentList, 'getNodeActions').and.returnValue([actionModel]); let file = new FileNode(); let actions = documentList.getContextActions(file); - expect(documentList.getContentActions).toHaveBeenCalledWith('document', 'menu'); + expect(documentList.getNodeActions).toHaveBeenCalledWith(file); expect(actions.length).toBe(1); expect(actions[0].model).toBe(actionModel); expect(actions[0].node).toBe(file); @@ -736,12 +629,12 @@ describe('DocumentList', () => { it('should fetch context menu actions for a folder node', () => { let actionModel = {}; - spyOn(documentList, 'getContentActions').and.returnValue([actionModel]); + spyOn(documentList, 'getNodeActions').and.returnValue([actionModel]); let folder = new FolderNode(); let actions = documentList.getContextActions(folder); - expect(documentList.getContentActions).toHaveBeenCalledWith('folder', 'menu'); + expect(documentList.getNodeActions).toHaveBeenCalledWith(folder); expect(actions.length).toBe(1); expect(actions[0].model).toBe(actionModel); expect(actions[0].node).toBe(folder); @@ -749,51 +642,39 @@ describe('DocumentList', () => { }); it('should fetch no context menu actions for unknown type', () => { - spyOn(documentList, 'getContentActions').and.stub(); + spyOn(documentList, 'getNodeActions').and.stub(); let node = new FileNode(); node.entry.isFile = false; node.entry.isFolder = false; let actions = documentList.getContextActions(node); - - expect(documentList.getContentActions).not.toHaveBeenCalled(); expect(actions).toBeNull(); }); it('should return null value when no content actions found', () => { - spyOn(documentList, 'getContentActions').and.returnValue([]); + spyOn(documentList, 'getNodeActions').and.returnValue([]); let file = new FileNode(); let actions = documentList.getContextActions(file); expect(actions).toBeNull(); - expect(documentList.getContentActions).toHaveBeenCalled(); + expect(documentList.getNodeActions).toHaveBeenCalled(); }); + /* it('should update error message when folder content display fails', () => { let error = 'My Error'; - alfrescoServiceMock.getFolderReject = true; - alfrescoServiceMock.getFolderRejectError = error; + documentListService.getFolderReject = true; + documentListService.getFolderRejectError = error; documentList.displayFolderContent('/some/path'); expect(documentList.errorMessage).toBe(error); }); + */ - it('should get object value via property path', () => { - let obj = { - name: { - firstName: '<name>' - } - }; - - expect(documentList.getObjectValue(obj, 'name.firstName')).toBe('<name>'); - }); - - it('should not get object value via invalid path', () => { - expect(documentList.getObjectValue({}, 'some.missing.path')).toBeUndefined(); - }); - + // TODO: move to data adapter + /* it('should log error when having date conversion issues', () => { let value = '<wrong-date>'; @@ -813,7 +694,10 @@ describe('DocumentList', () => { expect(result).toBe(value); expect(console.error).toHaveBeenCalledWith(`DocumentList: error parsing date ${value} to format ${col.format}`); }); + */ + // TODO: move to data adapter + /* it('should convert thumbnail if column source defined', () => { let file = new FileNode(); let col = new ContentColumnModel({ @@ -823,6 +707,7 @@ describe('DocumentList', () => { expect(documentList.getCellValue(file, col)).toBe(file.entry.name); }); + */ it('should require current folder path to reload', () => { @@ -839,6 +724,8 @@ describe('DocumentList', () => { expect(documentList.displayFolderContent).not.toHaveBeenCalled(); }); + // TODO: move to data adapter + /* it('should not sort empty page', () => { let page = new PageNode(); spyOn(page.list.entries, 'sort').and.stub(); @@ -846,7 +733,10 @@ describe('DocumentList', () => { documentList.sort(page, null); expect(page.list.entries.sort).not.toHaveBeenCalled(); }); + */ + // TODO: move to data adapter + /* it('should put folders to top on sort', () => { let folder = new FolderNode(); let file1 = new FileNode('file1'); @@ -873,7 +763,10 @@ describe('DocumentList', () => { expect(page.list.entries[1]).toBe(file2); expect(page.list.entries[2]).toBe(file1); }); + */ + // TODO: move to data adapter + /* it('should sort by dates up to ms', () => { let file1 = new FileNode(); file1.entry['dateProp'] = new Date(2016, 6, 30, 13, 14, 1); @@ -901,5 +794,6 @@ describe('DocumentList', () => { expect(page.list.entries[0]).toBe(file1); expect(page.list.entries[1]).toBe(file2); }); + */ }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts index f9f9d53a13..26bb14d6b9 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts @@ -22,19 +22,27 @@ import { Output, EventEmitter, AfterContentInit, + AfterViewInit, AfterViewChecked, OnChanges, TemplateRef, - NgZone + NgZone, + ViewChild } from '@angular/core'; -import { DatePipe } from '@angular/common'; import { Subject } from 'rxjs/Rx'; import { CONTEXT_MENU_DIRECTIVES } from 'ng2-alfresco-core'; -import { AlfrescoService } from './../services/alfresco.service'; -import { MinimalNodeEntity, NodePaging } from './../models/document-library.model'; + +import { + ALFRESCO_DATATABLE_DIRECTIVES, + DataRowEvent, + DataTableComponent, + ObjectDataColumn +} from 'ng2-alfresco-datatable'; + +import { DocumentListService } from './../services/document-list.service'; +import { MinimalNodeEntity } from './../models/document-library.model'; import { ContentActionModel } from './../models/content-action.model'; -import { ContentColumnModel } from './../models/content-column.model'; -import { ColumnSortingModel } from './../models/column-sorting.model'; +import { ShareDataTableAdapter, ShareDataRow } from './../data/share-datatable-adapter'; declare var componentHandler; declare let __moduleName: string; @@ -44,13 +52,13 @@ declare let __moduleName: string; selector: 'alfresco-document-list', styleUrls: ['./document-list.css'], templateUrl: './document-list.html', - providers: [AlfrescoService], - directives: [CONTEXT_MENU_DIRECTIVES], + providers: [DocumentListService], + directives: [CONTEXT_MENU_DIRECTIVES, ALFRESCO_DATATABLE_DIRECTIVES], host: { '(contextmenu)': 'onShowContextMenu($event)' } }) -export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, OnChanges { +export class DocumentList implements OnInit, AfterViewInit, AfterViewChecked, AfterContentInit, OnChanges { static SINGLE_CLICK_NAVIGATION: string = 'click'; static DOUBLE_CLICK_NAVIGATION: string = 'dblclick'; @@ -62,17 +70,26 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, @Input() navigate: boolean = true; - @Input('navigation-mode') + @Input() navigationMode: string = 'dblclick'; // click|dblclick @Input() thumbnails: boolean = false; - @Output() - itemClick: EventEmitter<any> = new EventEmitter(); + @Input() + multiselect: boolean = false; + + @Input() + contentActions: boolean = false; + + @Input() + contextMenuActions: boolean = false; @Output() - itemDblClick: EventEmitter<any> = new EventEmitter(); + nodeClick: EventEmitter<any> = new EventEmitter(); + + @Output() + nodeDblClick: EventEmitter<any> = new EventEmitter(); @Output() folderChange: EventEmitter<any> = new EventEmitter(); @@ -80,6 +97,9 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, @Output() preview: EventEmitter<any> = new EventEmitter(); + @ViewChild(DataTableComponent) + dataTable: DataTableComponent; + private _path = this.DEFAULT_ROOT_FOLDER; get currentFolderPath(): string { @@ -91,66 +111,36 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, if (value !== this._path) { this._path = value || this.DEFAULT_ROOT_FOLDER; this.displayFolderContent(this._path); - } - } - - errorMessage; - - actions: ContentActionModel[] = []; - columns: ContentColumnModel[] = []; - emptyFolderTemplate: TemplateRef<any>; - - private _folder: NodePaging; - - get folder(): NodePaging { - return this._folder; - } - - set folder(value: NodePaging) { - let isChanged = this._folder !== value; - this._folder = value; - if (isChanged) { this.folderChange.emit({ - value: value, path: this.currentFolderPath }); } } - sorting: ColumnSortingModel = { - key: 'name', - direction: 'asc' - }; - + errorMessage; + actions: ContentActionModel[] = []; + emptyFolderTemplate: TemplateRef<any>; contextActionHandler: Subject<any> = new Subject(); + data: ShareDataTableAdapter; constructor( - private alfrescoService: AlfrescoService, - private ngZone: NgZone) {} + private documentListService: DocumentListService, + private ngZone: NgZone) { + + this.data = new ShareDataTableAdapter(this.documentListService, this.baseComponentPath, []); + } getContextActions(node: MinimalNodeEntity) { if (node && node.entry) { - let targetType; - - if (node.entry.isFolder) { - targetType = 'folder'; - } - - if (node.entry.isFile) { - targetType = 'document'; - } - - if (targetType) { - let actions = this.getContentActions(targetType, 'menu'); - if (actions && actions.length > 0) { - return actions.map(a => { - return { - model: a, - node: node, - subject: this.contextActionHandler - }; - }); - } + let actions = this.getNodeActions(node); + if (actions && actions.length > 0) { + return actions.map(a => { + return { + model: a, + node: node, + subject: this.contextActionHandler + }; + }); } } return null; @@ -163,20 +153,31 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, } ngOnInit() { + this.data.thumbnails = this.thumbnails; this.displayFolderContent(this.currentFolderPath); this.contextActionHandler.subscribe(val => this.contextActionCallback(val)); } - ngOnChanges(change) { + ngOnChanges() { this.reload(); } ngAfterContentInit() { - if (!this.columns || this.columns.length === 0) { + let columns = this.data.getColumns(); + if (!columns || columns.length === 0) { this.setupDefaultColumns(); } } + ngAfterViewInit() { + if (this.dataTable) { + if (this.emptyFolderTemplate) { + this.dataTable.noContentTemplate = this.emptyFolderTemplate; + } + + } + } + ngAfterViewChecked() { // workaround for MDL issues with dynamic components if (componentHandler) { @@ -185,79 +186,31 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, } - /** - * Get a list of content actions based on target and type. - * @param target Target to filter actions by. - * @param type Type to filter actions by. - * @returns {ContentActionModel[]} List of actions filtered by target and type. - */ - getContentActions(target: string, type: string): ContentActionModel[] { - if (target && type) { + getNodeActions(node: MinimalNodeEntity): ContentActionModel[] { + let target = null; - let ltarget = target.toLowerCase(); - let ltype = type.toLowerCase(); + if (node && node.entry) { + if (node.entry.isFile) { + target = 'document'; + } - return this.actions.filter(entry => { - return entry.target.toLowerCase() === ltarget && - entry.type.toLowerCase() === ltype; - }); + if (node.entry.isFolder) { + target = 'folder'; + } + + if (target) { + + let ltarget = target.toLowerCase(); + + return this.actions.filter(entry => { + return entry.target.toLowerCase() === ltarget; + }); + } } + return []; } - /** - * Invoked when list row is clicked. - * @param item Underlying node item - * @param e DOM event (optional) - */ - onItemClick(item: MinimalNodeEntity, e?: Event) { - if (e) { - e.preventDefault(); - } - - this.itemClick.emit({ - value: item - }); - - if (this.navigate && this.navigationMode === DocumentList.SINGLE_CLICK_NAVIGATION) { - if (item && item.entry) { - if (item.entry.isFile) { - this.preview.emit({ - value: item - }); - } - - if (item.entry.isFolder) { - this.performNavigation(item); - } - } - } - } - - onItemDblClick(item: MinimalNodeEntity, e?: Event) { - if (e) { - e.preventDefault(); - } - - this.itemDblClick.emit({ - value: item - }); - - if (this.navigate && this.navigationMode === DocumentList.DOUBLE_CLICK_NAVIGATION) { - if (item && item.entry) { - if (item.entry.isFile) { - this.preview.emit({ - value: item - }); - } - - if (item.entry.isFolder) { - this.performNavigation(item); - } - } - } - } - onShowContextMenu(e?: Event) { if (e) { e.preventDefault(); @@ -272,41 +225,6 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, return false; } - /** - * Gets thumbnail URL for the given node. - * @param node Node to get URL for. - * @returns {string} URL address. - */ - getThumbnailUrl(node: MinimalNodeEntity): string { - if (node && node.entry) { - let entry = node.entry; - - if (entry.isFolder) { - return `${this.baseComponentPath}/img/ft_ic_folder.svg`; - } - - if (entry.isFile) { - if (this.thumbnails) { - if (this.alfrescoService) { - return this.alfrescoService.getDocumentThumbnailUrl(node); - } - return null; - } - - if (entry.content && entry.content.mimeType) { - let icon = this.alfrescoService.getMimeTypeIcon(entry.content.mimeType); - if (icon) { - return `${this.baseComponentPath}/img/${icon}`; - } - } - } - - return `${this.baseComponentPath}/img/ft_ic_miscellaneous.svg`; - } - - return null; - } - /** * Invoked when executing content action for a document or folder. * @param node Node to be the context of the execution. @@ -319,14 +237,7 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, } displayFolderContent(path: string) { - if (path) { - this.alfrescoService - .getFolder(path) - .subscribe( - folder => this.folder = this.sort(folder, this.sorting), - error => this.errorMessage = <any>error - ); - } + this.data.loadPath(path); } reload() { @@ -350,122 +261,109 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, return null; } - /** - * Gets a value from an object by composed key - * documentList.getObjectValue({ item: { nodeType: 'cm:folder' }}, 'item.nodeType') ==> 'cm:folder' - * @param target - * @param key - * @returns {string} - */ - getObjectValue(target: any, key: string): any { - let keys = key.split('.'); - key = ''; - - do { - key += keys.shift(); - let value = target[key]; - if (value !== undefined && (typeof value === 'object' || !keys.length)) { - target = value; - key = ''; - } else if (!keys.length) { - target = undefined; - } else { - key += '.'; - } - } while (keys.length); - - return target; - } - - getCellValue(row: MinimalNodeEntity, col: ContentColumnModel): any { - let value = this.getObjectValueRaw(row.entry, col.source); - - if (col.type === 'date') { - let datePipe = new DatePipe(); - try { - return datePipe.transform(value, col.format); - } catch (err) { - console.error(`DocumentList: error parsing date ${value} to format ${col.format}`); - } - } - - if (col.type === 'image') { - - if (col.source === '$thumbnail') { - return this.getThumbnailUrl(row); - } - - } - - return value; - } - /** * Creates a set of predefined columns. */ setupDefaultColumns(): void { - let thumbnailCol = new ContentColumnModel(); - thumbnailCol.source = '$thumbnail'; - thumbnailCol.type = 'image'; + let colThumbnail = new ObjectDataColumn({ + type: 'image', + key: '$thumbnail', + title: '', + srTitle: 'Thumbnail' + }); - let nameCol = new ContentColumnModel(); - nameCol.title = 'Name'; - nameCol.source = 'name'; - nameCol.cssClass = 'full-width name-column'; + let colName = new ObjectDataColumn({ + type: 'text', + key: 'name', + title: 'Name', + cssClass: 'full-width', + sortable: true + }); - this.columns = [ - thumbnailCol, - nameCol - ]; + this.data.setColumns([colThumbnail, colName]); } - onColumnHeaderClick(column: ContentColumnModel) { - if (column && this.isSortableColumn(column)) { - if (this.sorting.key === column.source) { - this.sorting.direction = this.sorting.direction === 'asc' ? 'desc' : 'asc'; - } else { - this.sorting = <ColumnSortingModel> { - key: column.source, - direction: 'asc' - }; - } - this.sort(this.folder, this.sorting); - } - } - - sort(node: NodePaging, options: ColumnSortingModel) { - if (this.hasEntries(node)) { - node.list.entries.sort((a: MinimalNodeEntity, b: MinimalNodeEntity) => { - if (a.entry.isFolder !== b.entry.isFolder) { - return a.entry.isFolder ? -1 : 1; - } - - let left = this.getObjectValueRaw(a.entry, options.key).toString(); - let right = this.getObjectValueRaw(b.entry, options.key).toString(); - - return options.direction === 'asc' - ? left.localeCompare(right) - : right.localeCompare(left); + onPreviewFile(node: MinimalNodeEntity) { + if (node) { + this.preview.emit({ + value: node }); } - return node; } - private getObjectValueRaw(target: any, key: string) { - let val = this.getObjectValue(target, key); + onNodeClick(node: MinimalNodeEntity) { + this.nodeClick.emit({ + value: node + }); - if (val instanceof Date) { - val = val.valueOf(); + if (this.navigate && this.navigationMode === DocumentList.SINGLE_CLICK_NAVIGATION) { + if (node && node.entry) { + if (node.entry.isFile) { + this.onPreviewFile(node); + } + + if (node.entry.isFolder) { + this.performNavigation(node); + } + } } - - return val; } - private hasEntries(node: NodePaging): boolean { - return (node && node.list && node.list.entries && node.list.entries.length > 0); + onRowClick(event: DataRowEvent) { + let item = (<ShareDataRow> event.value).node; + this.onNodeClick(item); } - private isSortableColumn(column: ContentColumnModel) { - return column && column.source && !column.source.startsWith('$'); + onNodeDblClick(node: MinimalNodeEntity) { + this.nodeDblClick.emit({ + value: node + }); + + if (this.navigate && this.navigationMode === DocumentList.DOUBLE_CLICK_NAVIGATION) { + if (node && node.entry) { + if (node.entry.isFile) { + this.onPreviewFile(node); + } + + if (node.entry.isFolder) { + this.performNavigation(node); + } + } + } } + + onRowDblClick(event?: DataRowEvent) { + let item = (<ShareDataRow> event.value).node; + this.onNodeDblClick(item); + } + + onShowRowContextMenu(event) { + if (this.contextMenuActions) { + let args = event.args; + let node = (<ShareDataRow> args.row).node; + if (node) { + args.actions = this.getContextActions(node) || []; + } + } + } + + onShowRowActionsMenu(event) { + if (this.contentActions) { + let args = event.args; + let node = (<ShareDataRow> args.row).node; + if (node) { + args.actions = this.getNodeActions(node) || []; + } + } + } + + onExecuteRowAction(event) { + if (this.contentActions) { + let args = event.args; + let node = (<ShareDataRow> args.row).node; + let action = (<ContentActionModel> args.action); + this.executeContentAction(node, action); + } + } + } diff --git a/ng2-components/ng2-alfresco-documentlist/src/data/share-datatable-adapter.ts b/ng2-components/ng2-alfresco-documentlist/src/data/share-datatable-adapter.ts new file mode 100644 index 0000000000..6944149cf1 --- /dev/null +++ b/ng2-components/ng2-alfresco-documentlist/src/data/share-datatable-adapter.ts @@ -0,0 +1,240 @@ +/*! + * @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 { DatePipe } from '@angular/common'; +import { + DataTableAdapter, + DataRow, DataColumn, DataSorting +} from 'ng2-alfresco-datatable'; + +import { NodePaging, MinimalNodeEntity } from './../models/document-library.model'; +import { DocumentListService } from './../services/document-list.service'; + +export class ShareDataTableAdapter implements DataTableAdapter { + + private sorting: DataSorting; + private rows: DataRow[]; + private columns: DataColumn[]; + + thumbnails: boolean = false; + + constructor(private documentListService: DocumentListService, + private basePath: string, + schema: DataColumn[]) { + this.rows = []; + this.columns = schema || []; + } + + getRows(): Array<DataRow> { + return this.rows; + } + + setRows(rows: Array<DataRow>) { + this.rows = rows || []; + this.sort(); + } + + getColumns(): Array<DataColumn> { + return this.columns; + } + + setColumns(columns: Array<DataColumn>) { + this.columns = columns || []; + } + + getValue(row: DataRow, col: DataColumn): any { + if (!row) { + throw new Error('Row not found'); + } + if (!col) { + throw new Error('Column not found'); + } + let value = row.getValue(col.key); + + if (col.type === 'date') { + let datePipe = new DatePipe(); + let format = col.format || 'medium'; + try { + return datePipe.transform(value, format); + } catch (err) { + console.error(`DocumentList: error parsing date ${value} to format ${format}`); + } + } + + if (col.type === 'image') { + + if (col.key === '$thumbnail') { + let node = (<ShareDataRow> row).node; + + if (node.entry.isFolder) { + return `${this.basePath}/img/ft_ic_folder.svg`; + } + + if (node.entry.isFile) { + + if (this.thumbnails) { + if (this.documentListService) { + return this.documentListService.getDocumentThumbnailUrl(node); + } + return null; + } + + if (node.entry.content && node.entry.content.mimeType) { + let mimeType = node.entry.content.mimeType; + if (mimeType) { + let icon = this.documentListService.getMimeTypeIcon(mimeType); + if (icon) { + return `${this.basePath}/img/${icon}`; + } + } + } + } + + return `${this.basePath}/img/ft_ic_miscellaneous.svg`; + } + + } + + return value; + } + + getSorting(): DataSorting { + return this.sorting; + } + + setSorting(sorting: DataSorting): void { + this.sorting = sorting; + + if (sorting && sorting.key) { + this.rows.sort((a: ShareDataRow, b: ShareDataRow) => { + if (a.node.entry.isFolder !== b.node.entry.isFolder) { + return a.node.entry.isFolder ? -1 : 1; + } + + let left = a.getValue(sorting.key); + if (left) { + left = (left instanceof Date) ? left.valueOf().toString() : left.toString(); + } else { + left = ''; + } + + let right = b.getValue(sorting.key); + if (right) { + right = (right instanceof Date) ? right.valueOf().toString() : right.toString(); + } else { + right = ''; + } + + return sorting.direction === 'asc' + ? left.localeCompare(right) + : right.localeCompare(left); + }); + } + } + + sort(key?: string, direction?: string): void { + let sorting = this.sorting || new DataSorting(); + if (key) { + sorting.key = key; + sorting.direction = direction || 'asc'; + } + this.setSorting(sorting); + } + + loadPath(path: string) { + if (path && this.documentListService) { + this.documentListService + .getFolder(path) + .subscribe(val => { + let page = <NodePaging>val; + let rows = []; + + if (page && page.list) { + let data = page.list.entries; + if (data && data.length > 0) { + rows = data.map(item => new ShareDataRow(item)); + // Sort by first sortable or just first column + let sortable = this.columns.filter(c => c.sortable); + if (sortable.length > 0) { + this.sort(sortable[0].key, 'asc'); + } else { + this.sort(this.columns[0].key, 'asc'); + } + } + } + + this.rows = rows; + }, + error => console.log(error)); + } + } + +} + +export class ShareDataRow implements DataRow { + isSelected: boolean = false; + + get node(): MinimalNodeEntity { + return this.obj; + } + + constructor(private obj: MinimalNodeEntity) { + if (!obj) { + throw new Error('Object source not found'); + } + } + + /** + * Gets a value from an object by composed key + * documentList.getObjectValue({ item: { nodeType: 'cm:folder' }}, 'item.nodeType') ==> 'cm:folder' + * @param target + * @param key + * @returns {string} + */ + getObjectValue(target: any, key: string): any { + + if (!target) { + return undefined; + } + + let keys = key.split('.'); + key = ''; + + do { + key += keys.shift(); + let value = target[key]; + if (value !== undefined && (typeof value === 'object' || !keys.length)) { + target = value; + key = ''; + } else if (!keys.length) { + target = undefined; + } else { + key += '.'; + } + } while (keys.length); + + return target; + } + + getValue(key: string): any { + return this.getObjectValue(this.obj.entry, key); + } + + hasValue(key: string): boolean { + return this.getValue(key) ? true : false; + } +} diff --git a/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts b/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts deleted file mode 100644 index 6f1f3b6dd5..0000000000 --- a/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts +++ /dev/null @@ -1,31 +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. - */ - -export class ColumnSortingModel { - - static DEFAULT_DIRECTION: string = 'asc'; - - key: string; - direction: string = ColumnSortingModel.DEFAULT_DIRECTION; - - constructor(opts?: any) { - if (opts) { - this.key = opts.key; - this.direction = opts.direction || ColumnSortingModel.DEFAULT_DIRECTION; - } - } -} diff --git a/ng2-components/ng2-alfresco-documentlist/src/models/content-action.model.ts b/ng2-components/ng2-alfresco-documentlist/src/models/content-action.model.ts index c8f01df746..fa568c8091 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/models/content-action.model.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/models/content-action.model.ts @@ -19,7 +19,6 @@ export class ContentActionModel { icon: string; title: string; handler: ContentActionHandler; - type: string; target: string; constructor(obj?: any) { @@ -27,7 +26,6 @@ export class ContentActionModel { this.icon = obj.icon; this.title = obj.title; this.handler = obj.handler; - this.type = obj.type; this.target = obj.target; } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/models/content-column.model.ts b/ng2-components/ng2-alfresco-documentlist/src/models/content-column.model.ts deleted file mode 100644 index e5612eb011..0000000000 --- a/ng2-components/ng2-alfresco-documentlist/src/models/content-column.model.ts +++ /dev/null @@ -1,51 +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. - */ - -export class ContentColumnModel { - - static TYPE_TEXT: string = 'text'; - static TYPE_DATE: string = 'date'; - static TYPE_IMAGE: string = 'image'; - // static TYPE_NUMBER: string = 'number'; - - title: string; - srTitle: string; - source: string; - cssClass: string; - type: string = ContentColumnModel.TYPE_TEXT; - format: string = 'medium'; - - constructor(obj?: any) { - if (obj) { - this.title = obj.title; - this.srTitle = obj.srTitle; - this.source = obj.source; - this.cssClass = obj.cssClass; - this.type = obj.type || ContentColumnModel.TYPE_TEXT; - this.format = obj.format; - } - } - - static getSupportedTypes(): string[] { - return [ - ContentColumnModel.TYPE_TEXT, - ContentColumnModel.TYPE_DATE, - ContentColumnModel.TYPE_IMAGE - // ContentColumnModel.TYPE_NUMBER - ]; - } -} diff --git a/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.spec.ts index 6a00dd19ae..fab08fc90c 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.spec.ts @@ -24,8 +24,8 @@ import { import { AlfrescoContentService } from 'ng2-alfresco-core'; import { ContentActionHandler } from '../models/content-action.model'; import { DocumentActionsService } from './document-actions.service'; -import { AlfrescoServiceMock } from '../assets/alfresco.service.mock'; -import { AlfrescoService } from './alfresco.service'; +import { DocumentListServiceMock } from '../assets/document-list.service.mock'; +import { DocumentListService } from './document-list.service'; import { FileNode, FolderNode @@ -34,13 +34,13 @@ import { describe('DocumentActionsService', () => { let service: DocumentActionsService; - let alfrescoService: AlfrescoService; + let documentListService: DocumentListService; let contentService: AlfrescoContentService; beforeEach(() => { - alfrescoService = new AlfrescoServiceMock(); + documentListService = new DocumentListServiceMock(); contentService = new AlfrescoContentService(null, null); - service = new DocumentActionsService(alfrescoService, contentService); + service = new DocumentActionsService(documentListService, contentService); }); it('should register default download action', () => { @@ -147,7 +147,7 @@ describe('DocumentActionsService', () => { }); it('should require content service for download action', () => { - let actionService = new DocumentActionsService(alfrescoService, null); + let actionService = new DocumentActionsService(documentListService, null); let file = new FileNode(); let result = actionService.getHandler('download')(file); expect(result).toBeFalsy(); @@ -159,44 +159,44 @@ describe('DocumentActionsService', () => { }); it('should delete file node', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let file = new FileNode(); service.getHandler('delete')(file); - expect(alfrescoService.deleteNode).toHaveBeenCalledWith(file.entry.id); + expect(documentListService.deleteNode).toHaveBeenCalledWith(file.entry.id); }); it('should support deletion only file node', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let folder = new FolderNode(); service.getHandler('delete')(folder); - expect(alfrescoService.deleteNode).not.toHaveBeenCalled(); + expect(documentListService.deleteNode).not.toHaveBeenCalled(); let file = new FileNode(); service.getHandler('delete')(file); - expect(alfrescoService.deleteNode).toHaveBeenCalled(); + expect(documentListService.deleteNode).toHaveBeenCalled(); }); it('should require node id to delete', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let file = new FileNode(); file.entry.id = null; service.getHandler('delete')(file); - expect(alfrescoService.deleteNode).not.toHaveBeenCalled(); + expect(documentListService.deleteNode).not.toHaveBeenCalled(); }); it('should reload target upon node deletion', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let target = jasmine.createSpyObj('obj', ['reload']); let file = new FileNode(); service.getHandler('delete')(file, target); - expect(alfrescoService.deleteNode).toHaveBeenCalled(); + expect(documentListService.deleteNode).toHaveBeenCalled(); expect(target.reload).toHaveBeenCalled(); }); }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.ts b/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.ts index 6699dd90dc..8927cdb9c2 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/services/document-actions.service.ts @@ -15,9 +15,9 @@ * limitations under the License. */ -import {Injectable} from '@angular/core'; -import {ContentActionHandler} from '../models/content-action.model'; -import {AlfrescoService} from './alfresco.service'; +import { Injectable } from '@angular/core'; +import { ContentActionHandler } from '../models/content-action.model'; +import { DocumentListService } from './document-list.service'; import { AlfrescoContentService } from 'ng2-alfresco-core'; @Injectable() @@ -25,7 +25,7 @@ export class DocumentActionsService { private handlers: { [id: string]: ContentActionHandler; } = {}; constructor( - private alfrescoService?: AlfrescoService, + private documentListService?: DocumentListService, private contentService?: AlfrescoContentService ) { this.setupActionHandlers(); @@ -49,7 +49,7 @@ export class DocumentActionsService { } canExecuteAction(obj: any): boolean { - return this.alfrescoService && obj && obj.entry.isFile === true; + return this.documentListService && obj && obj.entry.isFile === true; } private setupActionHandlers() { @@ -86,7 +86,7 @@ export class DocumentActionsService { private deleteNode(obj: any, target?: any) { if (this.canExecuteAction(obj) && obj.entry && obj.entry.id) { - this.alfrescoService.deleteNode(obj.entry.id).subscribe(() => { + this.documentListService.deleteNode(obj.entry.id).subscribe(() => { if (target && typeof target.reload === 'function') { target.reload(); } diff --git a/ng2-components/ng2-alfresco-documentlist/src/services/alfresco.service.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/services/document-list.service.spec.ts similarity index 79% rename from ng2-components/ng2-alfresco-documentlist/src/services/alfresco.service.spec.ts rename to ng2-components/ng2-alfresco-documentlist/src/services/document-list.service.spec.ts index 917513bf93..77acf68e69 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/services/alfresco.service.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/services/document-list.service.spec.ts @@ -27,12 +27,11 @@ import { AlfrescoContentService } from 'ng2-alfresco-core'; import { FileNode } from '../assets/document-library.model.mock'; -import { AlfrescoService } from './alfresco.service'; +import { DocumentListService } from './document-list.service'; -// TODO: rename to DocumentListService -describe('AlfrescoService', () => { +describe('DocumentListService', () => { - let service: AlfrescoService; + let service: DocumentListService; let settingsService: AlfrescoSettingsService; let authService: AlfrescoAuthenticationService; let contentService: AlfrescoContentService; @@ -42,7 +41,7 @@ describe('AlfrescoService', () => { settingsService = new AlfrescoSettingsService(); authService = new AlfrescoAuthenticationService(settingsService); contentService = new AlfrescoContentService(settingsService, authService); - service = new AlfrescoService(settingsService, authService, contentService); + service = new DocumentListService(settingsService, authService, contentService); }); it('should require node to get thumbnail url', () => { @@ -50,7 +49,7 @@ describe('AlfrescoService', () => { }); it('should require content service to get thumbnail url', () => { - service = new AlfrescoService(settingsService, authService, null); + service = new DocumentListService(settingsService, authService, null); let file = new FileNode(); expect(service.getDocumentThumbnailUrl(file)).toBeNull(); }); @@ -72,9 +71,9 @@ describe('AlfrescoService', () => { }); it('should resolve default icon for mime type', () => { - expect(service.getMimeTypeIcon(null)).toBe(AlfrescoService.DEFAULT_MIME_TYPE_ICON); - expect(service.getMimeTypeIcon('')).toBe(AlfrescoService.DEFAULT_MIME_TYPE_ICON); - expect(service.getMimeTypeIcon('missing/type')).toBe(AlfrescoService.DEFAULT_MIME_TYPE_ICON); + expect(service.getMimeTypeIcon(null)).toBe(DocumentListService.DEFAULT_MIME_TYPE_ICON); + expect(service.getMimeTypeIcon('')).toBe(DocumentListService.DEFAULT_MIME_TYPE_ICON); + expect(service.getMimeTypeIcon('missing/type')).toBe(DocumentListService.DEFAULT_MIME_TYPE_ICON); }); }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/services/alfresco.service.ts b/ng2-components/ng2-alfresco-documentlist/src/services/document-list.service.ts similarity index 95% rename from ng2-components/ng2-alfresco-documentlist/src/services/alfresco.service.ts rename to ng2-components/ng2-alfresco-documentlist/src/services/document-list.service.ts index 3ed57b5d81..6a78992fd7 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/services/alfresco.service.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/services/document-list.service.ts @@ -27,12 +27,8 @@ import { declare let AlfrescoApi: any; -// TODO: consider renaming to something like 'DocumentListService' -/** - * Internal service used by Document List component. - */ @Injectable() -export class AlfrescoService { +export class DocumentListService { static DEFAULT_MIME_TYPE_ICON: string = 'ft_ic_miscellaneous.svg'; @@ -120,7 +116,7 @@ export class AlfrescoService { getMimeTypeIcon(mimeType: string): string { let icon = this.mimeTypeIcons[mimeType]; - return icon || AlfrescoService.DEFAULT_MIME_TYPE_ICON; + return icon || DocumentListService.DEFAULT_MIME_TYPE_ICON; } private handleError(error: Response) { diff --git a/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.spec.ts index 31716f1f46..c40f5cddf8 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.spec.ts @@ -27,17 +27,17 @@ import { FileNode, FolderNode } from '../assets/document-library.model.mock'; -import { AlfrescoService } from './alfresco.service'; -import { AlfrescoServiceMock } from '../assets/alfresco.service.mock'; +import { DocumentListService } from './document-list.service'; +import { DocumentListServiceMock } from '../assets/document-list.service.mock'; describe('FolderActionsService', () => { let service: FolderActionsService; - let alfrescoService: AlfrescoService; + let documentListService: DocumentListService; beforeEach(() => { - alfrescoService = new AlfrescoServiceMock(); - service = new FolderActionsService(alfrescoService); + documentListService = new DocumentListServiceMock(); + service = new FolderActionsService(documentListService); }); it('should register custom action handler', () => { @@ -105,44 +105,44 @@ describe('FolderActionsService', () => { }); it('should delete folder node', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let folder = new FolderNode(); service.getHandler('delete')(folder); - expect(alfrescoService.deleteNode).toHaveBeenCalledWith(folder.entry.id); + expect(documentListService.deleteNode).toHaveBeenCalledWith(folder.entry.id); }); it('should support deletion only folder node', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let file = new FileNode(); service.getHandler('delete')(file); - expect(alfrescoService.deleteNode).not.toHaveBeenCalled(); + expect(documentListService.deleteNode).not.toHaveBeenCalled(); let folder = new FolderNode(); service.getHandler('delete')(folder); - expect(alfrescoService.deleteNode).toHaveBeenCalled(); + expect(documentListService.deleteNode).toHaveBeenCalled(); }); it('should require node id to delete', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let folder = new FolderNode(); folder.entry.id = null; service.getHandler('delete')(folder); - expect(alfrescoService.deleteNode).not.toHaveBeenCalled(); + expect(documentListService.deleteNode).not.toHaveBeenCalled(); }); it('should reload target upon node deletion', () => { - spyOn(alfrescoService, 'deleteNode').and.callThrough(); + spyOn(documentListService, 'deleteNode').and.callThrough(); let target = jasmine.createSpyObj('obj', ['reload']); let folder = new FolderNode(); service.getHandler('delete')(folder, target); - expect(alfrescoService.deleteNode).toHaveBeenCalled(); + expect(documentListService.deleteNode).toHaveBeenCalled(); expect(target.reload).toHaveBeenCalled(); }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.ts b/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.ts index a0e6fa7567..73d9a33868 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/services/folder-actions.service.ts @@ -15,15 +15,15 @@ * limitations under the License. */ -import {Injectable} from '@angular/core'; -import {ContentActionHandler} from '../models/content-action.model'; -import {AlfrescoService} from './alfresco.service'; +import { Injectable } from '@angular/core'; +import { ContentActionHandler } from '../models/content-action.model'; +import { DocumentListService } from './document-list.service'; @Injectable() export class FolderActionsService { private handlers: { [id: string]: ContentActionHandler; } = {}; - constructor(private _alfrescoService?: AlfrescoService) { + constructor(private documentListService?: DocumentListService) { this.setupActionHandlers(); } @@ -45,7 +45,7 @@ export class FolderActionsService { } canExecuteAction(obj: any): boolean { - return this._alfrescoService && obj && obj.entry.isFolder === true; + return this.documentListService && obj && obj.entry.isFolder === true; } private setupActionHandlers() { @@ -68,7 +68,7 @@ export class FolderActionsService { private deleteNode(obj: any, target?: any) { if (this.canExecuteAction(obj) && obj.entry && obj.entry.id) { - this._alfrescoService.deleteNode(obj.entry.id).subscribe(() => { + this.documentListService.deleteNode(obj.entry.id).subscribe(() => { if (target && typeof target.reload === 'function') { target.reload(); }