[ADF-1188] Upload related components, unify interfaces, deprecate disableWithNoPermission (#2216)

* Deprecate enabled input property in UploadDragAreaComponent

* Refactoring about disabled property

* using adf-node-permission directive, deprecating disableWithNoPermission

* Parent component access from directive

* Upliad drag area component uses the directive also

* Usage of property instead of helper method in the NodePermissionSubjects

* Fix small issues

* Update readme files
This commit is contained in:
Popovics András
2017-08-16 15:44:04 +02:00
committed by Mario Romano
parent 9cd188aa4f
commit ac55704220
12 changed files with 494 additions and 218 deletions

View File

@@ -127,11 +127,13 @@ export { CardViewModule } from './src/components/view/card-view.module';
export { CollapsableModule } from './src/components/collapsable/collapsable.module';
export { CardViewItem } from './src/interface/card-view-item.interface';
export { TimeAgoPipe } from './src/pipes/time-ago.pipe';
export { EXTENDIBLE_COMPONENT } from './src/interface/injection.tokens';
export * from './src/components/data-column/data-column.component';
export * from './src/components/data-column/data-column-list.component';
export * from './src/directives/upload.directive';
export * from './src/directives/highlight.directive';
export * from './src/directives/node-permission.directive';
export * from './src/utils/index';
export * from './src/events/base.event';
export * from './src/events/base-ui.event';

View File

@@ -15,98 +15,138 @@
* limitations under the License.
*/
import { ElementRef, SimpleChange } from '@angular/core';
import { Component, ElementRef, SimpleChange } from '@angular/core';
import { AlfrescoContentService } from './../services/alfresco-content.service';
import { NodePermissionDirective } from './node-permission.directive';
import { NodePermissionDirective, NodePermissionSubject } from './node-permission.directive';
@Component({
selector: 'adf-text-subject'
})
class TestComponent implements NodePermissionSubject {
disabled: boolean = false;
}
describe('NodePermissionDirective', () => {
it('updates element once it is loaded', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
describe('HTML nativeElement as subject', () => {
directive.ngAfterViewInit();
it('updates element once it is loaded', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
expect(directive.updateElement).toHaveBeenCalled();
directive.ngAfterViewInit();
expect(directive.updateElement).toHaveBeenCalled();
});
it('updates element on nodes change', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, false);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).toHaveBeenCalled();
});
it('updates element only on subsequent change', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, true);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).not.toHaveBeenCalled();
});
it('enables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['removeAttribute']);
const elementRef = new ElementRef({});
const directive = new NodePermissionDirective(elementRef, renderer, null);
directive.enableElement();
expect(renderer.removeAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled');
});
it('disables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['setAttribute']);
const elementRef = new ElementRef({});
const directive = new NodePermissionDirective(elementRef, renderer, null);
directive.disableElement();
expect(renderer.setAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled', 'true');
});
it('disables element when nodes not available', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = null;
expect(directive.updateElement()).toBeFalsy();
directive.nodes = [];
expect(directive.updateElement()).toBeFalsy();
});
it('enables element when all nodes have expected permission', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(true);
const directive = new NodePermissionDirective(null, null, contentService);
spyOn(directive, 'enableElement').and.stub();
directive.nodes = <any> [{}, {}];
expect(directive.updateElement()).toBeTruthy();
expect(directive.enableElement).toHaveBeenCalled();
});
it('disables element when one of the nodes have no permission', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(false);
const directive = new NodePermissionDirective(null, null, contentService);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = <any> [{}, {}];
expect(directive.updateElement()).toBeFalsy();
expect(directive.disableElement).toHaveBeenCalled();
});
});
it('updates element on nodes change', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
describe('Angular component as subject', () => {
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, false);
directive.ngOnChanges({ nodes: change });
it('disables decorated component', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(false);
expect(directive.updateElement).toHaveBeenCalled();
let testComponent = new TestComponent();
testComponent.disabled = false;
const directive = new NodePermissionDirective(null, null, contentService, testComponent);
directive.nodes = <any> [{}, {}];
directive.updateElement();
expect(testComponent.disabled).toBeTruthy();
});
it('enables decorated component', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(true);
let testComponent = new TestComponent();
testComponent.disabled = true;
const directive = new NodePermissionDirective(null, null, contentService, testComponent);
directive.nodes = <any> [{}, {}];
directive.updateElement();
expect(testComponent.disabled).toBeFalsy();
});
});
it('updates element only on subsequent change', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, true);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).not.toHaveBeenCalled();
});
it('enables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['removeAttribute']);
const elementRef = new ElementRef({});
const directive = new NodePermissionDirective(elementRef, renderer, null);
directive.enableElement();
expect(renderer.removeAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled');
});
it('disables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['setAttribute']);
const elementRef = new ElementRef({});
const directive = new NodePermissionDirective(elementRef, renderer, null);
directive.disableElement();
expect(renderer.setAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled', 'true');
});
it('disables element when nodes not available', () => {
const directive = new NodePermissionDirective(null, null, null);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = null;
expect(directive.updateElement()).toBeFalsy();
directive.nodes = [];
expect(directive.updateElement()).toBeFalsy();
});
it('enables element when all nodes have expected permission', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(true);
const directive = new NodePermissionDirective(null, null, contentService);
spyOn(directive, 'enableElement').and.stub();
directive.nodes = <any> [{}, {}];
expect(directive.updateElement()).toBeTruthy();
expect(directive.enableElement).toHaveBeenCalled();
});
it('disables element when one of the nodes have no permission', () => {
const contentService = new AlfrescoContentService(null, null, null);
spyOn(contentService, 'hasPermission').and.returnValue(false);
const directive = new NodePermissionDirective(null, null, contentService);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = <any> [{}, {}];
expect(directive.updateElement()).toBeFalsy();
expect(directive.disableElement).toHaveBeenCalled();
});
});

View File

@@ -15,10 +15,15 @@
* limitations under the License.
*/
import { AfterViewInit, Directive, ElementRef, Input, OnChanges, Renderer2, SimpleChanges } from '@angular/core';
import { AfterViewInit, Directive, ElementRef, Host, Inject, Input, OnChanges, Optional, Renderer2, SimpleChanges } from '@angular/core';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { EXTENDIBLE_COMPONENT } from './../interface/injection.tokens';
import { AlfrescoContentService } from './../services/alfresco-content.service';
export interface NodePermissionSubject {
disabled: boolean;
}
@Directive({
selector: '[adf-node-permission]'
})
@@ -32,7 +37,11 @@ export class NodePermissionDirective implements OnChanges, AfterViewInit {
constructor(private elementRef: ElementRef,
private renderer: Renderer2,
private contentService: AlfrescoContentService) {
private contentService: AlfrescoContentService,
@Host()
@Optional()
@Inject(EXTENDIBLE_COMPONENT) private parentComponent?: NodePermissionSubject) {
}
ngAfterViewInit() {
@@ -55,14 +64,30 @@ export class NodePermissionDirective implements OnChanges, AfterViewInit {
let enable = this.hasPermission(this.nodes, this.permission);
if (enable) {
this.enableElement();
this.enable();
} else {
this.disableElement();
this.disable();
}
return enable;
}
private enable(): void {
if (this.parentComponent) {
this.parentComponent.disabled = false;
} else {
this.enableElement();
}
}
private disable(): void {
if (this.parentComponent) {
this.parentComponent.disabled = true;
} else {
this.disableElement();
}
}
/**
* Enables decorated element
*

View File

@@ -1,5 +1,19 @@
# Node Permission Directive
<!-- markdown-toc start - Don't edit this section. npm run toc to generate it-->
<!-- toc -->
- [Properties](#properties)
- [HTML element example](#html-element-example)
- [Angular component example](#angular-component-example)
* [Implementing the NodePermissionSubject interface](#implementing-the-nodepermissionsubject-interface)
* [Defining your components as an EXTENDIBLE_COMPONENT parent component](#defining-your-components-as-an-extendible_component-parent-component)
<!-- tocstop -->
<!-- markdown-toc end -->
The `NodePermissionDirective` allows you to disable an HTML element or Angular component
by taking a collection of the `MinimalNodeEntity` instances and checking the particular permission.
@@ -8,7 +22,14 @@ The decorated element will be disabled if:
- there are no nodes in the collection
- at least one of the nodes has no expected permission
## Basic example
## Properties
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| adf-node-permission | [Permissions](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-alfresco-core/src/models/permissions.enum.ts) | null | Node permission to check (create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions)|
| adf-nodes | MinimalNodeEntity[] | [] | Nodes to check permission for |
## HTML element example
The best example to show `NodePermissionDirective` in action is by binding DocumentList selection property to a toolbar button.
@@ -30,9 +51,47 @@ For example the "Delete" button should be disabled if no selection is present or
The button will become disabled by default, and is going to change its state once user selects/unselects one or multiple documents that current user has permission to delete.
## Properties
## Angular component example
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| adf-node-permission | [Permissions](https://github.com/Alfresco/alfresco-ng2-components/blob/master/ng2-components/ng2-alfresco-core/src/models/permissions.enum.ts) | null | Node permission to check (create, delete, update, updatePermissions, !create, !delete, !update, !updatePermissions)|
| adf-nodes | MinimalNodeEntity[] | [] | Nodes to check permission for |
You can apply the directive on any angular component which implements the NodePermissionSubject interface. The upload drag area component can be a good candidate, since this one implements that interface. Applying the directive on an angular component is pretty much the same as applying it on an html element.
```html
<alfresco-upload-drag-area
[parentId]="..."
[versioning]="..."
[adf-node-permission]="'create'"
[adf-nodes]="getCurrentDocumentListNode()">
...
</alfresco-upload-drag-area>
```
When designing a component you want to work this directive with, you have two important things to care about.
### Implementing the NodePermissionSubject interface
The component has to implement the NodePermissionSubject interface which basically means it has to have a boolean **disabled** property. This is the property which will be set by the directive.
```js
import { NodePermissionSubject } from 'ng2-alfresco-core';
@Component({...})
export class UploadDragAreaComponent implements NodePermissionSubject {
public disabled: boolean = false;
}
```
### Defining your components as an EXTENDIBLE_COMPONENT parent component
The directive will look up the component in the dependency injection tree, up to at most one step above the current DI level (@Host). Because of this, you have to provide your component with forward referencing as the EXTENDIBLE_COMPONENT.
```js
import { EXTENDIBLE_COMPONENT } from 'ng2-alfresco-core';
@Component({
...
providers: [
{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent)}
]
})
export class UploadDragAreaComponent implements NodePermissionSubject { ... }
```

View File

@@ -0,0 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, InjectionToken } from '@angular/core';
export const EXTENDIBLE_COMPONENT = new InjectionToken<Component>('extendible.component');