[ACA-1508] extensions: wave 1 (#480)

* initial structure scaffold

* core extensions module

* simple navbar composition

* allow using app routes instead of registered

* migrate to new navbar setup

* remove commented out tests

* populate toolbar

* evaluate expressions

* redirect to url from toolbar

* populate "open with" viewer menu

* update test setup

* experimental flag for extensions

* test fixes

* fix tests

* code improvements, order support

* improve routing management

* populate "create" menu

* extra dictionaries for spellcheck

* allow disabling extension content

* support file/folder targets for toolbar actions

* add safety check

* navigate directly

* toolbar actions for all pages

* support route data

* "experimental" flag for "create" menu extensions

* code fixes
This commit is contained in:
Denys Vuika
2018-07-06 19:45:42 +01:00
committed by GitHub
parent 3e123bee62
commit e75042aa46
41 changed files with 865 additions and 141 deletions

View File

@@ -6,6 +6,14 @@
<mat-icon [title]="'APP.NEW_MENU.TOOLTIP' | translate">queue</mat-icon>
</div>
<div sidebar-menu-options>
<ng-container *ifExperimental="'extensions'">
<button *ngFor="let entry of createActions"
mat-menu-item
(click)="runAction(entry.action)">
<mat-icon>{{ entry.icon }}</mat-icon>
<span>{{ entry.title | translate }}</span>
</button>
</ng-container>
<button
mat-menu-item
[disabled]="!permission.check(node, ['create'])"
@@ -48,16 +56,17 @@
</adf-sidebar-action-menu>
</div>
<div class="sidenav__section sidenav__section--menu" *ngFor="let list of navigation">
<div class="sidenav__section sidenav__section--menu" *ngFor="let group of groups">
<ul class="sidenav-menu">
<li *ngFor="let item of list" class="sidenav-menu__item"
<li *ngFor="let item of group" class="sidenav-menu__item"
routerLinkActive
#rla="routerLinkActive"
title="{{ item.title || '' | translate }}">
title="{{ item.description | translate }}">
<button [routerLink]="item.route.url"
<button
[routerLink]="item.route"
[color]="rla.isActive ? 'accent': 'primary'"
[attr.aria-label]="item.label | translate"
[attr.aria-label]="item.title | translate"
mat-icon-button
mat-ripple
matRippleColor="primary"
@@ -68,13 +77,13 @@
</button>
<span #rippleTrigger
[routerLink]="item.route"
class="menu__item--label"
[routerLink]="item.route.url"
[hidden]="!showLabel"
[ngClass]="{
'menu__item--active': rla.isActive,
'menu__item--default': !rla.isActive
}">{{ item.label | translate }}</span>
}">{{ item.title | translate }}</span>
</li>
</ul>
</div>

View File

@@ -25,26 +25,17 @@
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { AppConfigService } from '@alfresco/adf-core';
import { BrowsingFilesService } from '../../common/services/browsing-files.service';
import { SidenavComponent } from './sidenav.component';
import { EffectsModule } from '@ngrx/effects';
import { NodeEffects } from '../../store/effects/node.effects';
import { AppTestingModule } from '../../testing/app-testing.module';
import { ExperimentalDirective } from '../../directives/experimental.directive';
describe('SidenavComponent', () => {
let fixture: ComponentFixture<SidenavComponent>;
let component: SidenavComponent;
let browsingService: BrowsingFilesService;
let appConfig: AppConfigService;
let appConfigSpy;
const navItem = {
label: 'some-label',
route: {
url: '/some-url'
}
};
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -53,19 +44,17 @@ describe('SidenavComponent', () => {
EffectsModule.forRoot([NodeEffects])
],
declarations: [
SidenavComponent
SidenavComponent,
ExperimentalDirective
],
schemas: [ NO_ERRORS_SCHEMA ]
})
.compileComponents()
.then(() => {
browsingService = TestBed.get(BrowsingFilesService);
appConfig = TestBed.get(AppConfigService);
fixture = TestBed.createComponent(SidenavComponent);
component = fixture.componentInstance;
appConfigSpy = spyOn(appConfig, 'get').and.returnValue([navItem]);
});
}));
@@ -77,20 +66,4 @@ describe('SidenavComponent', () => {
expect(component.node).toBe(node);
});
describe('menu', () => {
it('should build menu from array', () => {
appConfigSpy.and.returnValue([navItem, navItem]);
fixture.detectChanges();
expect(component.navigation).toEqual([[navItem, navItem]]);
});
it('should build menu from object', () => {
appConfigSpy.and.returnValue({ a: [navItem, navItem], b: [navItem, navItem] });
fixture.detectChanges();
expect(component.navigation).toEqual([[navItem, navItem], [navItem, navItem]]);
});
});
});

View File

@@ -26,11 +26,11 @@
import { Subscription } from 'rxjs/Rx';
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { AppConfigService } from '@alfresco/adf-core';
import { BrowsingFilesService } from '../../common/services/browsing-files.service';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { ExtensionService } from '../../extensions/extension.service';
import { NavigationExtension } from '../../extensions/navigation.extension';
import { CreateExtension } from '../../extensions/create.extension';
@Component({
selector: 'app-sidenav',
@@ -41,22 +41,25 @@ export class SidenavComponent implements OnInit, OnDestroy {
@Input() showLabel: boolean;
node: MinimalNodeEntryEntity = null;
navigation = [];
groups: Array<NavigationExtension[]> = [];
createActions: Array<CreateExtension> = [];
private subscriptions: Subscription[] = [];
constructor(
private browsingFilesService: BrowsingFilesService,
private appConfig: AppConfigService,
public permission: NodePermissionService
public permission: NodePermissionService,
private extensions: ExtensionService
) {}
ngOnInit() {
this.navigation = this.buildMenu();
this.groups = this.extensions.getNavigationGroups();
this.createActions = this.extensions.createActions;
this.subscriptions.concat([
this.browsingFilesService.onChangeParent
.subscribe((node: MinimalNodeEntryEntity) => this.node = node)
this.browsingFilesService.onChangeParent.subscribe(
(node: MinimalNodeEntryEntity) => (this.node = node)
)
]);
}
@@ -64,10 +67,9 @@ export class SidenavComponent implements OnInit, OnDestroy {
this.subscriptions.forEach(s => s.unsubscribe());
}
private buildMenu() {
const schema = this.appConfig.get('navigation');
const data = Array.isArray(schema) ? { main: schema } : schema;
return Object.keys(data).map((key) => data[key]);
// this is where each application decides how to treat an action and what to do
// the ACA maps actions to the NgRx actions as an example
runAction(actionId: string) {
this.extensions.runActionById(actionId);
}
}