mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-24 17:31:52 +00:00
[ACA-2320] Navigation - support store actions (#1052)
* move component into folder * update module * add children template references * clean up styling * clean up theme * use content projection * remove old tests * button menu component * expand menu component * link item component * resolve action directive * custom active link directive * collapse template reference * expanded template reference * expansion panel directive * item template directive * menu panel directive * support for ngrx actions * update side navigation inplementation * remove unused component * remove unused styling * update module * clean up * unit tests * unit tests * remove unused component * lint * remove unused import * fix test * add tooltip * fix text * fix e2e * use action route commands * remove fdescribe * styles fix * e2e fix tooltip test * fix active route when drill down * update docs
This commit is contained in:
committed by
Denys Vuika
parent
9f127c0530
commit
839c9d0dbb
@@ -138,6 +138,30 @@ In the `app.config.json` define a link entry which will point to the custom page
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This can also be declared using ngrx store action:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
...,
|
||||||
|
"navigation": [
|
||||||
|
"main": [ ... ],
|
||||||
|
"secondary": [ ... ],
|
||||||
|
"custom": [
|
||||||
|
{
|
||||||
|
"icon": "work",
|
||||||
|
"label": "Link",
|
||||||
|
"title": "My custom link",
|
||||||
|
"click": {
|
||||||
|
"action": "NAVIGATE_ROUTE",
|
||||||
|
"payload": "custom-route"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
Map the `/custom-route` in `app.routes.ts` as a child of `LayoutComponent` definition.
|
Map the `/custom-route` in `app.routes.ts` as a child of `LayoutComponent` definition.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@@ -32,13 +32,14 @@ import { Utils } from '../../utilities/utils';
|
|||||||
export class Sidenav extends Component {
|
export class Sidenav extends Component {
|
||||||
private static selectors = {
|
private static selectors = {
|
||||||
root: 'app-sidenav',
|
root: 'app-sidenav',
|
||||||
link: '.menu__item',
|
link: '.item',
|
||||||
label: '.item--label',
|
label: '.action-button__label',
|
||||||
expansion_panel: ".mat-expansion-panel-header",
|
expansion_panel: ".mat-expansion-panel-header",
|
||||||
expansion_panel_content: ".mat-expansion-panel-body",
|
expansion_panel_content: ".mat-expansion-panel-body",
|
||||||
active: 'mat-accent',
|
active: 'mat-accent',
|
||||||
activeClass: '.item--active',
|
activeClass: '.action-button--active',
|
||||||
activeChild: 'item--active',
|
activeClassName: 'action-button--active',
|
||||||
|
activeChild: 'action-button--active',
|
||||||
|
|
||||||
newButton: '[data-automation-id="create-button"]',
|
newButton: '[data-automation-id="create-button"]',
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ export class Sidenav extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async isActive(name: string) {
|
async isActive(name: string) {
|
||||||
return await this.getLinkLabel(name).isElementPresent(by.css(Sidenav.selectors.activeClass));
|
return (await this.getLinkLabel(name).getAttribute('class')).includes(Sidenav.selectors.activeClassName);
|
||||||
}
|
}
|
||||||
|
|
||||||
async childIsActive(name: string) {
|
async childIsActive(name: string) {
|
||||||
|
@@ -61,22 +61,22 @@ describe('Sidebar', () => {
|
|||||||
it('My Libraries is automatically selected on expanding File Libraries - [C289900]', async () => {
|
it('My Libraries is automatically selected on expanding File Libraries - [C289900]', async () => {
|
||||||
await sidenav.expandFileLibraries();
|
await sidenav.expandFileLibraries();
|
||||||
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
|
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
|
||||||
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(false, 'File Libraries link is active');
|
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries is not active');
|
||||||
expect(await sidenav.childIsActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active');
|
expect(await sidenav.isActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('navigate to Favorite Libraries - [C289902]', async () => {
|
it('navigate to Favorite Libraries - [C289902]', async () => {
|
||||||
await page.goToFavoriteLibraries();
|
await page.goToFavoriteLibraries();
|
||||||
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITE_LIBRARIES);
|
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITE_LIBRARIES);
|
||||||
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(false, 'File Libraries link is active');
|
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link is not active');
|
||||||
expect(await sidenav.childIsActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toBe(true, 'Favorite Libraries link not active');
|
expect(await sidenav.isActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toBe(true, 'Favorite Libraries link not active');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('navigate to My Libraries - [C289901]', async () => {
|
it('navigate to My Libraries - [C289901]', async () => {
|
||||||
await page.goToMyLibraries();
|
await page.goToMyLibraries();
|
||||||
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
|
expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES);
|
||||||
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(false, 'File Libraries link is active');
|
expect(await sidenav.isActive(SIDEBAR_LABELS.FILE_LIBRARIES)).toBe(true, 'File Libraries link is not active');
|
||||||
expect(await sidenav.childIsActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active');
|
expect(await sidenav.isActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('navigates to "Shared Files" - [C213110]', async () => {
|
it('navigates to "Shared Files" - [C213110]', async () => {
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
<ng-container *ngIf="!expanded">
|
<ng-container *ngIf="!expanded">
|
||||||
<button
|
<button
|
||||||
color="accent"
|
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
class="app-create-menu--collapsed"
|
class="app-create-menu--collapsed"
|
||||||
data-automation-id="create-button"
|
data-automation-id="create-button"
|
||||||
@@ -25,7 +24,7 @@
|
|||||||
title="{{ 'APP.NEW_MENU.TOOLTIP' | translate }}"
|
title="{{ 'APP.NEW_MENU.TOOLTIP' | translate }}"
|
||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
[color]="createMenu.menuOpen ? 'accent' : 'primary'"
|
class="app-create-menu--icon"
|
||||||
title="{{ 'APP.NEW_MENU.TOOLTIP' | translate }}"
|
title="{{ 'APP.NEW_MENU.TOOLTIP' | translate }}"
|
||||||
>queue</mat-icon
|
>queue</mat-icon
|
||||||
>
|
>
|
||||||
|
@@ -52,13 +52,17 @@
|
|||||||
color: mat-color($foreground, text, 0.54);
|
color: mat-color($foreground, text, 0.54);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: mat-color($primary);
|
color: mat-color($accent);
|
||||||
}
|
}
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-create-menu--icon {
|
||||||
|
color: mat-color($accent);
|
||||||
|
}
|
||||||
|
|
||||||
&__sub-menu {
|
&__sub-menu {
|
||||||
.mat-menu-item {
|
.mat-menu-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@@ -21,12 +21,13 @@
|
|||||||
<adf-sidenav-layout-navigation>
|
<adf-sidenav-layout-navigation>
|
||||||
<ng-template let-isMenuMinimized="isMenuMinimized">
|
<ng-template let-isMenuMinimized="isMenuMinimized">
|
||||||
<app-sidenav
|
<app-sidenav
|
||||||
[showLabel]="!isMenuMinimized()"
|
[mode]="isMenuMinimized() ? 'collapsed' : 'expanded'"
|
||||||
[attr.data-automation-id]="
|
[attr.data-automation-id]="
|
||||||
isMenuMinimized() ? 'collapsed' : 'expanded'
|
isMenuMinimized() ? 'collapsed' : 'expanded'
|
||||||
"
|
"
|
||||||
(swipeleft)="hideMenu($event)"
|
(swipeleft)="hideMenu($event)"
|
||||||
></app-sidenav>
|
>
|
||||||
|
</app-sidenav>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</adf-sidenav-layout-navigation>
|
</adf-sidenav-layout-navigation>
|
||||||
|
|
||||||
|
@@ -0,0 +1,50 @@
|
|||||||
|
<ng-container *ngIf="!item.children">
|
||||||
|
<button
|
||||||
|
class="action-button"
|
||||||
|
mat-icon-button
|
||||||
|
acaActiveLink="action-button--active"
|
||||||
|
[action]="item"
|
||||||
|
[id]="item.id"
|
||||||
|
[attr.aria-label]="item.title | translate"
|
||||||
|
[attr.title]="item.description | translate"
|
||||||
|
[attr.data-automation-id]="item.id"
|
||||||
|
>
|
||||||
|
<adf-icon [value]="item.icon"></adf-icon>
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="item.children && item.children.length">
|
||||||
|
<button
|
||||||
|
[matMenuTriggerFor]="menu"
|
||||||
|
[acaMenuPanel]="item"
|
||||||
|
#acaMenuPanel="acaMenuPanel"
|
||||||
|
mat-icon-button
|
||||||
|
[id]="item.id"
|
||||||
|
[attr.data-automation-id]="item.id"
|
||||||
|
[attr.title]="item.description | translate"
|
||||||
|
[attr.aria-label]="item.title | translate"
|
||||||
|
class="action-button"
|
||||||
|
[ngClass]="{
|
||||||
|
'action-button--active': acaMenuPanel.hasActiveLinks()
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<adf-icon [value]="item.icon"></adf-icon>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<mat-menu #menu="matMenu" [overlapTrigger]="false">
|
||||||
|
<button
|
||||||
|
*ngFor="let child of item.children; trackBy: trackById"
|
||||||
|
acaActiveLink="action-button--active"
|
||||||
|
[action]="child"
|
||||||
|
[attr.aria-label]="child.title | translate"
|
||||||
|
[id]="child.id"
|
||||||
|
[attr.title]="child.description | translate"
|
||||||
|
[attr.data-automation-id]="child.id"
|
||||||
|
mat-menu-item
|
||||||
|
class="action-button"
|
||||||
|
>
|
||||||
|
<adf-icon *ngIf="child.icon" [value]="child.icon"></adf-icon>
|
||||||
|
<span class="action-button__label">{{ child.title | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</ng-container>
|
@@ -0,0 +1,99 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ButtonMenuComponent } from './button-menu.component';
|
||||||
|
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||||
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import {
|
||||||
|
TranslateModule,
|
||||||
|
TranslateLoader,
|
||||||
|
TranslateFakeLoader
|
||||||
|
} from '@ngx-translate/core';
|
||||||
|
import { AppSidenavModule } from '../sidenav.module';
|
||||||
|
|
||||||
|
describe('ButtonMenuComponent', () => {
|
||||||
|
let component: ButtonMenuComponent;
|
||||||
|
let fixture: ComponentFixture<ButtonMenuComponent>;
|
||||||
|
let router: Router;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
AppTestingModule,
|
||||||
|
AppSidenavModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ButtonMenuComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
router = TestBed.get(Router);
|
||||||
|
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render action item', () => {
|
||||||
|
component.item = {
|
||||||
|
id: 'test-action-button',
|
||||||
|
url: 'dummy'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const actionButton = document.body.querySelector('#test-action-button');
|
||||||
|
expect(actionButton).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render action item with children', () => {
|
||||||
|
component.item = {
|
||||||
|
id: 'test-action-button',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'child-1',
|
||||||
|
title: 'child-1',
|
||||||
|
url: 'dummy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'child-2',
|
||||||
|
title: 'child-2',
|
||||||
|
url: 'dummy'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const actionButton = document.body.querySelector(
|
||||||
|
'[id="test-action-button"]'
|
||||||
|
);
|
||||||
|
actionButton.dispatchEvent(new Event('click'));
|
||||||
|
|
||||||
|
expect(document.querySelector('[id="child-1"]')).not.toBeNull();
|
||||||
|
expect(document.querySelector('[id="child-2"]')).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
ViewEncapsulation,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from '@angular/core';
|
||||||
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-button-menu',
|
||||||
|
templateUrl: './button-menu.component.html',
|
||||||
|
host: { class: 'app-button-menu' },
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ButtonMenuComponent implements OnInit {
|
||||||
|
@Input() item;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cd: ChangeDetectorRef,
|
||||||
|
private overlayContainer: OverlayContainer
|
||||||
|
) {
|
||||||
|
this.overlayContainer.getContainerElement().classList.add('aca-menu-panel');
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.cd.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
trackById(index: number, obj: { id: string }) {
|
||||||
|
return obj.id;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
<ng-container *ngIf="!item.children">
|
||||||
|
<div class="item">
|
||||||
|
<button
|
||||||
|
acaActiveLink="action-button--active"
|
||||||
|
[action]="item"
|
||||||
|
[attr.aria-label]="item.title | translate"
|
||||||
|
[id]="item.id"
|
||||||
|
[attr.data-automation-id]="item.id"
|
||||||
|
[attr.title]="item.description | translate"
|
||||||
|
mat-button
|
||||||
|
class="action-button full-width"
|
||||||
|
>
|
||||||
|
<adf-icon *ngIf="item.icon" [value]="item.icon"></adf-icon>
|
||||||
|
<span class="action-button__label">{{ item.title | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="item.children && item.children.length">
|
||||||
|
<mat-expansion-panel
|
||||||
|
[expanded]="acaExpansionPanel.hasActiveLinks()"
|
||||||
|
[acaExpansionPanel]="item"
|
||||||
|
#acaExpansionPanel="acaExpansionPanel"
|
||||||
|
[@.disabled]="true"
|
||||||
|
>
|
||||||
|
<mat-expansion-panel-header expandedHeight="48px" collapsedHeight="48px">
|
||||||
|
<mat-panel-title>
|
||||||
|
<div class="item">
|
||||||
|
<button
|
||||||
|
[ngClass]="{
|
||||||
|
'action-button--active': acaExpansionPanel.hasActiveLinks()
|
||||||
|
}"
|
||||||
|
[attr.aria-label]="item.title | translate"
|
||||||
|
[id]="item.id"
|
||||||
|
[attr.title]="item.description | translate"
|
||||||
|
[attr.data-automation-id]="item.id"
|
||||||
|
mat-button
|
||||||
|
class="action-button full-width"
|
||||||
|
>
|
||||||
|
<adf-icon *ngIf="item.icon" [value]="item.icon"></adf-icon>
|
||||||
|
<span class="action-button__label">{{
|
||||||
|
item.title | translate
|
||||||
|
}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
|
<div *ngFor="let child of item.children; trackBy: trackById" class="item">
|
||||||
|
<button
|
||||||
|
acaActiveLink="action-button--active"
|
||||||
|
[action]="child"
|
||||||
|
[attr.aria-label]="child.title | translate"
|
||||||
|
[id]="child.id"
|
||||||
|
[attr.data-automation-id]="child.id"
|
||||||
|
[attr.title]="child.description | translate"
|
||||||
|
mat-button
|
||||||
|
class="action-button full-width"
|
||||||
|
>
|
||||||
|
<adf-icon *ngIf="child.icon" [value]="child.icon"></adf-icon>
|
||||||
|
<span class="action-button__label">{{ child.title | translate }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</ng-container>
|
@@ -0,0 +1,99 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ExpandMenuComponent } from './expand-menu.component';
|
||||||
|
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||||
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import {
|
||||||
|
TranslateModule,
|
||||||
|
TranslateLoader,
|
||||||
|
TranslateFakeLoader
|
||||||
|
} from '@ngx-translate/core';
|
||||||
|
import { AppSidenavModule } from '../sidenav.module';
|
||||||
|
|
||||||
|
describe('ExpandMenuComponent', () => {
|
||||||
|
let component: ExpandMenuComponent;
|
||||||
|
let fixture: ComponentFixture<ExpandMenuComponent>;
|
||||||
|
let router: Router;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
AppTestingModule,
|
||||||
|
AppSidenavModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ExpandMenuComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
router = TestBed.get(Router);
|
||||||
|
|
||||||
|
spyOn(router, 'navigate');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render action item', () => {
|
||||||
|
component.item = {
|
||||||
|
id: 'test-action-button',
|
||||||
|
url: 'dummy'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const actionButton = document.body.querySelector('#test-action-button');
|
||||||
|
expect(actionButton).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render action item with children', () => {
|
||||||
|
component.item = {
|
||||||
|
id: 'test-action-button',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 'child-1',
|
||||||
|
title: 'child-1',
|
||||||
|
url: 'dummy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'child-2',
|
||||||
|
title: 'child-2',
|
||||||
|
url: 'dummy'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const actionButton = document.body.querySelector(
|
||||||
|
'[id="test-action-button"]'
|
||||||
|
);
|
||||||
|
actionButton.dispatchEvent(new Event('click'));
|
||||||
|
|
||||||
|
expect(document.querySelector('[id="child-1"]')).not.toBeNull();
|
||||||
|
expect(document.querySelector('[id="child-2"]')).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,52 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
ViewEncapsulation,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-expand-menu',
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
templateUrl: './expand-menu.component.html',
|
||||||
|
host: { class: 'app-expand-menu' }
|
||||||
|
})
|
||||||
|
export class ExpandMenuComponent implements OnInit {
|
||||||
|
@Input() item;
|
||||||
|
|
||||||
|
constructor(private cd: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.cd.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
trackById(index: number, obj: { id: string }) {
|
||||||
|
return obj.id;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ActionDirective } from './action.directive';
|
||||||
|
|
||||||
|
describe('ActionDirective', () => {
|
||||||
|
let directive: ActionDirective;
|
||||||
|
const routeMock = <any>{
|
||||||
|
navigate: jasmine.createSpy('navigate'),
|
||||||
|
parseUrl: () => ({
|
||||||
|
root: {
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const storeMock = <any>{
|
||||||
|
dispatch: jasmine.createSpy('dispatch')
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
directive = new ActionDirective(routeMock, storeMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate if action is route', () => {
|
||||||
|
directive.action = { route: 'dummy' };
|
||||||
|
directive.onClick();
|
||||||
|
expect(routeMock.navigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch store action', () => {
|
||||||
|
directive.action = { click: {} };
|
||||||
|
directive.onClick();
|
||||||
|
expect(storeMock.dispatch).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
72
src/app/components/sidenav/directives/action.directive.ts
Normal file
72
src/app/components/sidenav/directives/action.directive.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Directive, Input, HostListener } from '@angular/core';
|
||||||
|
import { PRIMARY_OUTLET, Router } from '@angular/router';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppStore } from '../../../store/states/app.state';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
/* tslint:disable-next-line */
|
||||||
|
selector: '[action]',
|
||||||
|
exportAs: 'action'
|
||||||
|
})
|
||||||
|
export class ActionDirective {
|
||||||
|
@Input() action;
|
||||||
|
|
||||||
|
@HostListener('click')
|
||||||
|
onClick() {
|
||||||
|
if (this.action.route) {
|
||||||
|
this.router.navigate(this.getNavigationCommands(this.action.route));
|
||||||
|
} else if (this.action.click) {
|
||||||
|
this.dispatchAction(this.action.click);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private router: Router, private store: Store<AppStore>) {}
|
||||||
|
|
||||||
|
private dispatchAction(action) {
|
||||||
|
this.store.dispatch({
|
||||||
|
type: action.action,
|
||||||
|
payload: this.getNavigationCommands(action.payload)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNavigationCommands(url: string): any[] {
|
||||||
|
const urlTree = this.router.parseUrl(url);
|
||||||
|
const urlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
|
||||||
|
|
||||||
|
if (!urlSegmentGroup) {
|
||||||
|
return [url];
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlSegments = urlSegmentGroup.segments;
|
||||||
|
|
||||||
|
return urlSegments.reduce(function(acc, item) {
|
||||||
|
acc.push(item.path, item.parameters);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,109 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { AppSidenavModule } from '../sidenav.module';
|
||||||
|
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||||
|
import { AppTestingModule } from '../../../testing/app-testing.module';
|
||||||
|
import { Router, NavigationEnd } from '@angular/router';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-test-component',
|
||||||
|
template: `
|
||||||
|
<span
|
||||||
|
id="test-element"
|
||||||
|
acaActiveLink="active-link-class"
|
||||||
|
[action]="item"
|
||||||
|
></span>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
item = {
|
||||||
|
route: 'dummy'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockRouter {
|
||||||
|
private subject = new Subject();
|
||||||
|
events = this.subject.asObservable();
|
||||||
|
url = '';
|
||||||
|
|
||||||
|
navigateByUrl(url: string) {
|
||||||
|
const navigationEnd = new NavigationEnd(0, '', url);
|
||||||
|
this.subject.next(navigationEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ActionDirective', () => {
|
||||||
|
let fixture: ComponentFixture<TestComponent>;
|
||||||
|
let router: Router;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [AppTestingModule, AppSidenavModule],
|
||||||
|
declarations: [TestComponent],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: Router,
|
||||||
|
useClass: MockRouter
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(TestComponent);
|
||||||
|
router = TestBed.get(Router);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add active route class name', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
router.navigateByUrl('/dummy');
|
||||||
|
// fixture.detectChanges();
|
||||||
|
expect(
|
||||||
|
document.body
|
||||||
|
.querySelector('#test-element')
|
||||||
|
.className.includes('active-link-class')
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove class name if route not active', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
router.navigateByUrl('/dummy');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
document.body
|
||||||
|
.querySelector('#test-element')
|
||||||
|
.className.includes('active-link-class')
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
router.navigateByUrl('/other');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
document.body
|
||||||
|
.querySelector('#test-element')
|
||||||
|
.className.includes('active-link-class')
|
||||||
|
).not.toBe(true);
|
||||||
|
});
|
||||||
|
});
|
@@ -27,57 +27,71 @@ import {
|
|||||||
Directive,
|
Directive,
|
||||||
OnInit,
|
OnInit,
|
||||||
Input,
|
Input,
|
||||||
HostListener,
|
ElementRef,
|
||||||
OnDestroy
|
Renderer2,
|
||||||
|
ContentChildren,
|
||||||
|
QueryList,
|
||||||
|
AfterContentInit
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Router, NavigationEnd } from '@angular/router';
|
import { Router, NavigationEnd } from '@angular/router';
|
||||||
import { filter, takeUntil } from 'rxjs/operators';
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { MatExpansionPanel } from '@angular/material/expansion';
|
import { ActionDirective } from './action.directive';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[acaExpansionPanel]',
|
selector: '[acaActiveLink]',
|
||||||
exportAs: 'acaExpansionPanel'
|
exportAs: 'acaActiveLink'
|
||||||
})
|
})
|
||||||
export class AcaExpansionPanelDirective implements OnInit, OnDestroy {
|
export class ActiveLinkDirective implements OnInit, AfterContentInit {
|
||||||
@Input() acaExpansionPanel;
|
@Input() acaActiveLink;
|
||||||
selected = false;
|
@ContentChildren(ActionDirective, { descendants: true })
|
||||||
|
links: QueryList<ActionDirective>;
|
||||||
|
isLinkActive = false;
|
||||||
|
|
||||||
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
||||||
|
|
||||||
@HostListener('click')
|
|
||||||
onClick() {
|
|
||||||
if (this.expansionPanel.expanded && !this.selected) {
|
|
||||||
this.router.navigate([this.acaExpansionPanel.children[0].url]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private expansionPanel: MatExpansionPanel
|
private element: ElementRef,
|
||||||
|
private renderer: Renderer2
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.setSelected(this.router.url);
|
|
||||||
|
|
||||||
this.router.events
|
this.router.events
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(event => event instanceof NavigationEnd),
|
filter(event => event instanceof NavigationEnd),
|
||||||
takeUntil(this.onDestroy$)
|
takeUntil(this.onDestroy$)
|
||||||
)
|
)
|
||||||
.subscribe((event: NavigationEnd) => {
|
.subscribe((event: NavigationEnd) => {
|
||||||
this.setSelected(event.urlAfterRedirects);
|
this.update(event.urlAfterRedirects);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
private update(url: string) {
|
||||||
this.onDestroy$.next(true);
|
this.links.map(item => {
|
||||||
this.onDestroy$.complete();
|
const itemUrl = this.resolveUrl(item);
|
||||||
|
if (url && url.substring(1).startsWith(itemUrl)) {
|
||||||
|
this.isLinkActive = true;
|
||||||
|
this.renderer.addClass(this.element.nativeElement, this.acaActiveLink);
|
||||||
|
} else {
|
||||||
|
this.isLinkActive = false;
|
||||||
|
this.renderer.removeClass(
|
||||||
|
this.element.nativeElement,
|
||||||
|
this.acaActiveLink
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setSelected(url: string) {
|
ngAfterContentInit() {
|
||||||
this.selected = this.acaExpansionPanel.children.some(child =>
|
this.links.changes.subscribe(() => this.update(this.router.url));
|
||||||
url.startsWith(child.url)
|
this.update(this.router.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveUrl(item): string {
|
||||||
|
return (
|
||||||
|
(item.action && (item.action.click && item.action.click.payload)) ||
|
||||||
|
item.action.route
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
31
src/app/components/sidenav/directives/collapsed-template.directive.ts
Executable file
31
src/app/components/sidenav/directives/collapsed-template.directive.ts
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Directive } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[acaCollapsedTemplate]'
|
||||||
|
})
|
||||||
|
export class CollapsedTemplateDirective {}
|
31
src/app/components/sidenav/directives/expanded-template.directive.ts
Executable file
31
src/app/components/sidenav/directives/expanded-template.directive.ts
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Directive } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[acaExpandedTemplate]'
|
||||||
|
})
|
||||||
|
export class ExpandedTemplateDirective {}
|
@@ -0,0 +1,139 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NavigationEnd } from '@angular/router';
|
||||||
|
import { ExpansionPanelDirective } from './expansion-panel.directive';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
class RouterStub {
|
||||||
|
url;
|
||||||
|
private subject = new Subject();
|
||||||
|
events = this.subject.asObservable();
|
||||||
|
|
||||||
|
constructor(url = 'some-url') {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseUrl() {
|
||||||
|
return {
|
||||||
|
root: {
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate(nextUrl: string) {
|
||||||
|
const navigationEnd = new NavigationEnd(0, this.url, nextUrl);
|
||||||
|
this.subject.next(navigationEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('AcaExpansionPanel', () => {
|
||||||
|
const mockStore = <any>{
|
||||||
|
dispatch: jasmine.createSpy('dispatch')
|
||||||
|
};
|
||||||
|
const mockMatExpansionPanel = <any>{
|
||||||
|
expanded: false,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('hasActiveLinks()', () => {
|
||||||
|
it('should return true if child is active route', () => {
|
||||||
|
const router: any = new RouterStub('dummy-route-2');
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
const directive = new ExpansionPanelDirective(
|
||||||
|
mockStore,
|
||||||
|
router,
|
||||||
|
mockMatExpansionPanel
|
||||||
|
);
|
||||||
|
|
||||||
|
directive.acaExpansionPanel = item;
|
||||||
|
|
||||||
|
expect(directive.hasActiveLinks()).toBe(true);
|
||||||
|
});
|
||||||
|
it('should return false if no child is active route', () => {
|
||||||
|
const router: any = new RouterStub('other');
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
const directive = new ExpansionPanelDirective(
|
||||||
|
mockStore,
|
||||||
|
router,
|
||||||
|
mockMatExpansionPanel
|
||||||
|
);
|
||||||
|
|
||||||
|
directive.acaExpansionPanel = item;
|
||||||
|
|
||||||
|
expect(directive.hasActiveLinks()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('navigation', () => {
|
||||||
|
it('should navigate to first child if none is active route', () => {
|
||||||
|
const router: any = new RouterStub('other');
|
||||||
|
spyOn(router, 'navigate').and.callThrough();
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
mockMatExpansionPanel.expanded = true;
|
||||||
|
|
||||||
|
const directive = new ExpansionPanelDirective(
|
||||||
|
mockStore,
|
||||||
|
router,
|
||||||
|
mockMatExpansionPanel
|
||||||
|
);
|
||||||
|
|
||||||
|
directive.acaExpansionPanel = item;
|
||||||
|
|
||||||
|
directive.onClick();
|
||||||
|
|
||||||
|
expect(router.navigate).toHaveBeenCalledWith(['dummy-route-1']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not navigate to first child if one is active route', () => {
|
||||||
|
const router: any = new RouterStub('dummy-route-2');
|
||||||
|
spyOn(router, 'navigate').and.callThrough();
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const directive = new ExpansionPanelDirective(
|
||||||
|
mockStore,
|
||||||
|
router,
|
||||||
|
mockMatExpansionPanel
|
||||||
|
);
|
||||||
|
|
||||||
|
directive.acaExpansionPanel = item;
|
||||||
|
mockMatExpansionPanel.expanded = true;
|
||||||
|
|
||||||
|
directive.onClick();
|
||||||
|
|
||||||
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,113 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
Input,
|
||||||
|
HostListener,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Router, NavigationEnd, PRIMARY_OUTLET } from '@angular/router';
|
||||||
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { MatExpansionPanel } from '@angular/material/expansion';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppStore } from '../../../store/states/app.state';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[acaExpansionPanel]',
|
||||||
|
exportAs: 'acaExpansionPanel'
|
||||||
|
})
|
||||||
|
export class ExpansionPanelDirective implements OnInit, OnDestroy {
|
||||||
|
@Input() acaExpansionPanel;
|
||||||
|
public hasActiveChildren = false;
|
||||||
|
|
||||||
|
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
||||||
|
|
||||||
|
@HostListener('click')
|
||||||
|
onClick() {
|
||||||
|
if (this.expansionPanel.expanded && !this.hasActiveLinks()) {
|
||||||
|
const firstChild = this.acaExpansionPanel.children[0];
|
||||||
|
if (firstChild.url) {
|
||||||
|
this.router.navigate(this.getNavigationCommands(firstChild.url));
|
||||||
|
} else {
|
||||||
|
this.store.dispatch({
|
||||||
|
type: firstChild.action.action,
|
||||||
|
payload: this.getNavigationCommands(firstChild.action.payload)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private store: Store<AppStore>,
|
||||||
|
private router: Router,
|
||||||
|
private expansionPanel: MatExpansionPanel
|
||||||
|
) {}
|
||||||
|
|
||||||
|
hasActiveLinks() {
|
||||||
|
if (this.acaExpansionPanel && this.acaExpansionPanel.children) {
|
||||||
|
return this.acaExpansionPanel.children.some(child => {
|
||||||
|
return this.router.url.startsWith(child.url || child.action.payload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.hasActiveChildren = this.hasActiveLinks();
|
||||||
|
|
||||||
|
this.router.events
|
||||||
|
.pipe(
|
||||||
|
filter(event => event instanceof NavigationEnd),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.hasActiveChildren = this.hasActiveLinks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNavigationCommands(url: string): any[] {
|
||||||
|
const urlTree = this.router.parseUrl(url);
|
||||||
|
const urlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
|
||||||
|
|
||||||
|
if (!urlSegmentGroup) {
|
||||||
|
return [url];
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlSegments = urlSegmentGroup.segments;
|
||||||
|
|
||||||
|
return urlSegments.reduce(function(acc, item) {
|
||||||
|
acc.push(item.path, item.parameters);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,123 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NavigationEnd } from '@angular/router';
|
||||||
|
import { MenuPanelDirective } from './menu-panel.directive';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
class RouterStub {
|
||||||
|
url;
|
||||||
|
private subject = new Subject();
|
||||||
|
events = this.subject.asObservable();
|
||||||
|
|
||||||
|
constructor(url = 'some-url') {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseUrl() {
|
||||||
|
return {
|
||||||
|
root: {
|
||||||
|
children: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate(nextUrl: string) {
|
||||||
|
const navigationEnd = new NavigationEnd(0, this.url, nextUrl);
|
||||||
|
this.subject.next(navigationEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('MenuPanelDirective', () => {
|
||||||
|
const mockStore = <any>{
|
||||||
|
dispatch: jasmine.createSpy('dispatch')
|
||||||
|
};
|
||||||
|
const mockMatExpansionPanel = <any>{
|
||||||
|
expanded: false,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('hasActiveLinks()', () => {
|
||||||
|
it('should return true if child is active route', () => {
|
||||||
|
const router: any = new RouterStub('dummy-route-2');
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
const directive = new MenuPanelDirective(mockStore, router);
|
||||||
|
|
||||||
|
directive.acaMenuPanel = item;
|
||||||
|
|
||||||
|
expect(directive.hasActiveLinks()).toBe(true);
|
||||||
|
});
|
||||||
|
it('should return false if no child is active route', () => {
|
||||||
|
const router: any = new RouterStub('other');
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
const directive = new MenuPanelDirective(mockStore, router);
|
||||||
|
|
||||||
|
directive.acaMenuPanel = item;
|
||||||
|
|
||||||
|
expect(directive.hasActiveLinks()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('navigation', () => {
|
||||||
|
it('should navigate to first child if none is active route', () => {
|
||||||
|
const router: any = new RouterStub('other');
|
||||||
|
spyOn(router, 'navigate').and.callThrough();
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
mockMatExpansionPanel.expanded = true;
|
||||||
|
|
||||||
|
const directive = new MenuPanelDirective(mockStore, router);
|
||||||
|
|
||||||
|
directive.acaMenuPanel = item;
|
||||||
|
|
||||||
|
directive.menuOpened();
|
||||||
|
|
||||||
|
expect(router.navigate).toHaveBeenCalledWith(['dummy-route-1']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not navigate to first child if one is active route', () => {
|
||||||
|
const router: any = new RouterStub('dummy-route-2');
|
||||||
|
spyOn(router, 'navigate').and.callThrough();
|
||||||
|
const item = {
|
||||||
|
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
const directive = new MenuPanelDirective(mockStore, router);
|
||||||
|
|
||||||
|
directive.acaMenuPanel = item;
|
||||||
|
mockMatExpansionPanel.expanded = true;
|
||||||
|
|
||||||
|
directive.menuOpened();
|
||||||
|
|
||||||
|
expect(router.navigate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
108
src/app/components/sidenav/directives/menu-panel.directive.ts
Normal file
108
src/app/components/sidenav/directives/menu-panel.directive.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* This file is part of the Alfresco Example Content Application.
|
||||||
|
* If the software was purchased under a paid Alfresco license, the terms of
|
||||||
|
* the paid license agreement will prevail. Otherwise, the software is
|
||||||
|
* provided under the following open source license terms:
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
HostListener
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Router, NavigationEnd, PRIMARY_OUTLET } from '@angular/router';
|
||||||
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppStore } from '../../../store/states/app.state';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[acaMenuPanel]',
|
||||||
|
exportAs: 'acaMenuPanel'
|
||||||
|
})
|
||||||
|
export class MenuPanelDirective implements OnInit, OnDestroy {
|
||||||
|
@Input() acaMenuPanel;
|
||||||
|
hasActiveChildren = false;
|
||||||
|
|
||||||
|
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
||||||
|
|
||||||
|
@HostListener('menuOpened')
|
||||||
|
menuOpened() {
|
||||||
|
if (this.acaMenuPanel.children && !this.hasActiveLinks()) {
|
||||||
|
const firstChild = this.acaMenuPanel.children[0];
|
||||||
|
if (firstChild.url) {
|
||||||
|
this.router.navigate(this.getNavigationCommands(firstChild.url));
|
||||||
|
} else {
|
||||||
|
this.store.dispatch({
|
||||||
|
type: firstChild.action.action,
|
||||||
|
payload: this.getNavigationCommands(firstChild.action.payload)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private store: Store<AppStore>, private router: Router) {}
|
||||||
|
|
||||||
|
hasActiveLinks() {
|
||||||
|
if (this.acaMenuPanel && this.acaMenuPanel.children) {
|
||||||
|
return this.acaMenuPanel.children.some(child => {
|
||||||
|
return this.router.url.startsWith(child.url || child.action.payload);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.hasActiveChildren = this.hasActiveLinks();
|
||||||
|
|
||||||
|
this.router.events
|
||||||
|
.pipe(
|
||||||
|
filter(event => event instanceof NavigationEnd),
|
||||||
|
takeUntil(this.onDestroy$)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.hasActiveChildren = this.hasActiveLinks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next(true);
|
||||||
|
this.onDestroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private getNavigationCommands(url: string): any[] {
|
||||||
|
const urlTree = this.router.parseUrl(url);
|
||||||
|
const urlSegmentGroup = urlTree.root.children[PRIMARY_OUTLET];
|
||||||
|
|
||||||
|
if (!urlSegmentGroup) {
|
||||||
|
return [url];
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlSegments = urlSegmentGroup.segments;
|
||||||
|
|
||||||
|
return urlSegments.reduce(function(acc, item) {
|
||||||
|
acc.push(item.path, item.parameters);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,119 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Alfresco Example Content Application
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 - 2019 Alfresco Software Limited
|
|
||||||
*
|
|
||||||
* This file is part of the Alfresco Example Content Application.
|
|
||||||
* If the software was purchased under a paid Alfresco license, the terms of
|
|
||||||
* the paid license agreement will prevail. Otherwise, the software is
|
|
||||||
* provided under the following open source license terms:
|
|
||||||
*
|
|
||||||
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { NavigationEnd } from '@angular/router';
|
|
||||||
import { AcaExpansionPanelDirective } from './expansion-panel.directive';
|
|
||||||
import { Subject } from 'rxjs';
|
|
||||||
|
|
||||||
class RouterStub {
|
|
||||||
url;
|
|
||||||
private subject = new Subject();
|
|
||||||
events = this.subject.asObservable();
|
|
||||||
|
|
||||||
constructor(url = 'some-url') {
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
navigate(nextUrl: string) {
|
|
||||||
const navigationEnd = new NavigationEnd(0, this.url, nextUrl);
|
|
||||||
this.subject.next(navigationEnd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('AcaExpansionPanel', () => {
|
|
||||||
const item = {
|
|
||||||
children: [{ url: 'dummy-route-1' }, { url: 'dummy-route-2' }]
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should set panel as selected on initialization if url contains child url', () => {
|
|
||||||
const router: any = new RouterStub('dummy-route-2');
|
|
||||||
const directive = new AcaExpansionPanelDirective(router, null);
|
|
||||||
|
|
||||||
directive.acaExpansionPanel = item;
|
|
||||||
directive.ngOnInit();
|
|
||||||
|
|
||||||
expect(directive.selected).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not set panel as selected on initialization if url does not contain child url', () => {
|
|
||||||
const router: any = new RouterStub('dummy-route-other');
|
|
||||||
const directive = new AcaExpansionPanelDirective(router, null);
|
|
||||||
|
|
||||||
directive.acaExpansionPanel = item;
|
|
||||||
directive.ngOnInit();
|
|
||||||
|
|
||||||
expect(directive.selected).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should go on first child url when expended and url does not contain any child url', () => {
|
|
||||||
const router: any = new RouterStub();
|
|
||||||
spyOn(router, 'navigate');
|
|
||||||
const expansionPanelInstance: any = { expanded: true };
|
|
||||||
const directive = new AcaExpansionPanelDirective(
|
|
||||||
router,
|
|
||||||
expansionPanelInstance
|
|
||||||
);
|
|
||||||
directive.acaExpansionPanel = item;
|
|
||||||
|
|
||||||
directive.ngOnInit();
|
|
||||||
directive.onClick();
|
|
||||||
|
|
||||||
expect(router.navigate).toHaveBeenCalledWith(['dummy-route-1']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not go on first child url when expended and url contains any child url', () => {
|
|
||||||
const router: any = new RouterStub('dummy-route-2');
|
|
||||||
spyOn(router, 'navigate');
|
|
||||||
const expansionPanelInstance: any = { expanded: true };
|
|
||||||
const directive = new AcaExpansionPanelDirective(
|
|
||||||
router,
|
|
||||||
expansionPanelInstance
|
|
||||||
);
|
|
||||||
directive.acaExpansionPanel = item;
|
|
||||||
|
|
||||||
directive.ngOnInit();
|
|
||||||
directive.onClick();
|
|
||||||
|
|
||||||
expect(router.navigate).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set panel selected on navigation change', done => {
|
|
||||||
const router: any = new RouterStub();
|
|
||||||
const directive = new AcaExpansionPanelDirective(router, null);
|
|
||||||
directive.acaExpansionPanel = item;
|
|
||||||
|
|
||||||
directive.ngOnInit();
|
|
||||||
|
|
||||||
router.navigate('dummy-route-1');
|
|
||||||
done();
|
|
||||||
|
|
||||||
expect(directive.selected).toBe(true);
|
|
||||||
|
|
||||||
router.navigate('some-url');
|
|
||||||
done();
|
|
||||||
|
|
||||||
expect(directive.selected).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,216 +1,48 @@
|
|||||||
<div class="sidenav">
|
<div class="sidenav">
|
||||||
<div class="section action-menu">
|
<ng-container [ngSwitch]="mode">
|
||||||
<app-create-menu [expanded]="showLabel"></app-create-menu>
|
<div class="section action-menu" [ngClass]="'section--' + mode">
|
||||||
|
<app-create-menu [expanded]="mode === 'expanded'"></app-create-menu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let group of groups; trackBy: trackById" class="section">
|
|
||||||
<div class="menu">
|
|
||||||
<div
|
<div
|
||||||
|
*ngFor="let group of groups; trackBy: trackById"
|
||||||
|
class="section"
|
||||||
|
[ngClass]="'section--' + mode"
|
||||||
|
>
|
||||||
|
<ng-container *ngSwitchCase="'expanded'">
|
||||||
|
<mat-list-item *ngFor="let item of group.items; trackBy: trackById">
|
||||||
|
<ng-container *ngIf="expandedTemplate">
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="expandedTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: item }"
|
||||||
|
>
|
||||||
|
</ng-template>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="!expandedTemplate">
|
||||||
|
<app-expand-menu [item]="item"></app-expand-menu>
|
||||||
|
</ng-container>
|
||||||
|
</mat-list-item>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngSwitchCase="'collapsed'">
|
||||||
|
<div
|
||||||
|
class="list-item"
|
||||||
*ngFor="let item of group.items; trackBy: trackById"
|
*ngFor="let item of group.items; trackBy: trackById"
|
||||||
routerLinkActive
|
|
||||||
#routerLink="routerLinkActive"
|
|
||||||
>
|
>
|
||||||
<ng-container *ngIf="showLabel">
|
<ng-container *ngIf="collapsedTemplate">
|
||||||
<ng-container *ngIf="!item.children">
|
<ng-template
|
||||||
<div
|
[ngTemplateOutlet]="collapsedTemplate"
|
||||||
class="menu__item"
|
[ngTemplateOutletContext]="{ $implicit: item }"
|
||||||
[attr.title]="item.description | translate"
|
|
||||||
[attr.data-automation-id]="item.id"
|
|
||||||
>
|
>
|
||||||
<button
|
</ng-template>
|
||||||
[id]="item.id"
|
|
||||||
mat-icon-button
|
|
||||||
mat-ripple
|
|
||||||
[routerLink]="item.url"
|
|
||||||
[color]="routerLink.isActive ? 'accent' : 'primary'"
|
|
||||||
[attr.aria-label]="item.title | translate"
|
|
||||||
matRippleColor="primary"
|
|
||||||
[matRippleTrigger]="rippleTrigger"
|
|
||||||
[matRippleCentered]="true"
|
|
||||||
[matRippleRadius]="20"
|
|
||||||
>
|
|
||||||
<adf-icon
|
|
||||||
[color]="routerLink.isActive ? 'accent' : 'primary'"
|
|
||||||
[value]="item.icon"
|
|
||||||
></adf-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<span
|
|
||||||
#rippleTrigger
|
|
||||||
class="item--label item--parent"
|
|
||||||
[routerLink]="item.url"
|
|
||||||
[attr.aria-label]="item.title | translate"
|
|
||||||
[ngClass]="{
|
|
||||||
'item--active': routerLink.isActive,
|
|
||||||
'item--default': !routerLink.isActive
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ item.title | translate }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="item.children && item.children.length">
|
<ng-container *ngIf="!collapsedTemplate">
|
||||||
<mat-expansion-panel
|
<app-button-menu [item]="item"></app-button-menu>
|
||||||
#expansionPanel="matExpansionPanel"
|
|
||||||
[acaExpansionPanel]="item"
|
|
||||||
[expanded]="routerLink.isActive"
|
|
||||||
[@.disabled]="true"
|
|
||||||
>
|
|
||||||
<mat-expansion-panel-header
|
|
||||||
expandedHeight="48px"
|
|
||||||
collapsedHeight="48px"
|
|
||||||
>
|
|
||||||
<mat-panel-title
|
|
||||||
[attr.title]="item.description | translate"
|
|
||||||
[attr.data-automation-id]="item.id"
|
|
||||||
>
|
|
||||||
<adf-icon
|
|
||||||
[color]="
|
|
||||||
routerLink.isActive && !expansionPanel.expanded
|
|
||||||
? 'accent'
|
|
||||||
: 'primary'
|
|
||||||
"
|
|
||||||
[value]="item.icon"
|
|
||||||
></adf-icon>
|
|
||||||
<span
|
|
||||||
class="item--label item--parent"
|
|
||||||
[id]="item.id"
|
|
||||||
[ngClass]="{
|
|
||||||
'item--default':
|
|
||||||
!routerLink.isActive && expansionPanel.expanded,
|
|
||||||
'item--active':
|
|
||||||
routerLink.isActive && !expansionPanel.expanded
|
|
||||||
}"
|
|
||||||
>{{ item.title | translate }}</span
|
|
||||||
>
|
|
||||||
</mat-panel-title>
|
|
||||||
</mat-expansion-panel-header>
|
|
||||||
|
|
||||||
<div
|
|
||||||
*ngFor="let child of item.children; trackBy: trackById"
|
|
||||||
routerLinkActive
|
|
||||||
#childRouteActive="routerLinkActive"
|
|
||||||
[attr.title]="child.description | translate"
|
|
||||||
[attr.data-automation-id]="child.id"
|
|
||||||
>
|
|
||||||
<ng-container *ngIf="child.icon">
|
|
||||||
<button
|
|
||||||
[id]="child.id"
|
|
||||||
mat-icon-button
|
|
||||||
mat-ripple
|
|
||||||
[routerLink]="child.url"
|
|
||||||
[color]="childRouteActive.isActive ? 'accent' : 'primary'"
|
|
||||||
[attr.aria-label]="child.title | translate"
|
|
||||||
matRippleColor="primary"
|
|
||||||
[matRippleTrigger]="rippleTrigger"
|
|
||||||
[matRippleCentered]="true"
|
|
||||||
[matRippleRadius]="20"
|
|
||||||
>
|
|
||||||
<adf-icon [value]="item.icon"></adf-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<span
|
|
||||||
#rippleTrigger
|
|
||||||
[routerLink]="child.url"
|
|
||||||
class="item--label item--label__trigger"
|
|
||||||
[ngClass]="{
|
|
||||||
'item--active': childRouteActive.isActive,
|
|
||||||
'item--default': !childRouteActive.isActive
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ child.title | translate }}
|
|
||||||
</span>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="!child.icon">
|
|
||||||
<div
|
|
||||||
[id]="child.id"
|
|
||||||
class="menu__item item--label item--child"
|
|
||||||
[routerLink]="child.url"
|
|
||||||
[attr.aria-label]="child.title | translate"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
[ngClass]="{
|
|
||||||
'item--active': childRouteActive.isActive,
|
|
||||||
'item--default': !childRouteActive.isActive
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ child.title | translate }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</mat-expansion-panel>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngIf="!showLabel">
|
|
||||||
<ng-container *ngIf="!item.children">
|
|
||||||
<div class="menu__item">
|
|
||||||
<button
|
|
||||||
[id]="item.id"
|
|
||||||
mat-icon-button
|
|
||||||
[routerLink]="item.url"
|
|
||||||
[color]="routerLink.isActive ? 'accent' : 'primary'"
|
|
||||||
[attr.aria-label]="item.title | translate"
|
|
||||||
[attr.title]="item.description | translate"
|
|
||||||
[attr.data-automation-id]="item.id"
|
|
||||||
>
|
|
||||||
<adf-icon
|
|
||||||
[color]="routerLink.isActive ? 'accent' : 'primary'"
|
|
||||||
[value]="item.icon"
|
|
||||||
></adf-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngIf="item.children && item.children.length">
|
|
||||||
<div
|
|
||||||
class="menu__item"
|
|
||||||
[attr.title]="item.description | translate"
|
|
||||||
[attr.data-automation-id]="item.id"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
[id]="item.id"
|
|
||||||
color="accent"
|
|
||||||
mat-icon-button
|
|
||||||
#childMenu="matMenuTrigger"
|
|
||||||
[matMenuTriggerFor]="menu"
|
|
||||||
>
|
|
||||||
<adf-icon
|
|
||||||
[color]="
|
|
||||||
routerLink.isActive || childMenu.menuOpen
|
|
||||||
? 'accent'
|
|
||||||
: 'primary'
|
|
||||||
"
|
|
||||||
[value]="item.icon"
|
|
||||||
></adf-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-menu #menu="matMenu" [overlapTrigger]="false">
|
|
||||||
<button
|
|
||||||
mat-menu-item
|
|
||||||
*ngFor="let child of item.children; trackBy: trackById"
|
|
||||||
routerLinkActive
|
|
||||||
#menuRouterLink="routerLinkActive"
|
|
||||||
[routerLink]="child.url"
|
|
||||||
[attr.title]="child.description | translate"
|
|
||||||
[id]="child.id"
|
|
||||||
[attr.data-automation-id]="child.id"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="mat-button"
|
|
||||||
[ngClass]="{ 'mat-primary': menuRouterLink.isActive }"
|
|
||||||
>
|
|
||||||
{{ child.title | translate }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</mat-menu>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,3 +1,10 @@
|
|||||||
|
.app-sidenav {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.sidenav {
|
.sidenav {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -8,10 +15,6 @@
|
|||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
|
||||||
padding: 8px 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-menu {
|
.action-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -19,14 +22,50 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.section.action-menu {
|
||||||
display: flex;
|
padding: 8px 14px;
|
||||||
flex-direction: column;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu__item {
|
.section {
|
||||||
|
padding: 8px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section--collapsed {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
padding: 12px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button .action-button__label {
|
||||||
|
margin-left: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-item,
|
||||||
|
.app-item .item {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -34,41 +73,27 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
width: 100%;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item--parent {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--label {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 240px;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--child {
|
|
||||||
padding-left: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--label:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--label__trigger {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-expansion-panel-header {
|
.mat-expansion-panel-header {
|
||||||
padding: 0 8px !important;
|
padding: 0 8px 0 0 !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-expansion-panel-header-title span {
|
.mat-expansion-panel {
|
||||||
margin-left: 8px;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-expansion-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-expansion-panel-body {
|
.mat-expansion-panel-body {
|
||||||
padding: 0 24px 0px;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-expansion-panel-header-title {
|
.mat-expansion-panel-header-title {
|
||||||
|
@@ -27,7 +27,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
|
|||||||
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
|
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
|
||||||
import { SidenavComponent } from './sidenav.component';
|
import { SidenavComponent } from './sidenav.component';
|
||||||
import { AppTestingModule } from '../../testing/app-testing.module';
|
import { AppTestingModule } from '../../testing/app-testing.module';
|
||||||
import { MatExpansionModule } from '@angular/material/expansion';
|
|
||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
|
|
||||||
describe('SidenavComponent', () => {
|
describe('SidenavComponent', () => {
|
||||||
@@ -46,7 +45,7 @@ describe('SidenavComponent', () => {
|
|||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [MatExpansionModule, AppTestingModule],
|
imports: [AppTestingModule],
|
||||||
providers: [AppExtensionService],
|
providers: [AppExtensionService],
|
||||||
declarations: [SidenavComponent],
|
declarations: [SidenavComponent],
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
@@ -6,11 +6,27 @@
|
|||||||
|
|
||||||
$border: 1px solid mat-color($foreground, divider, 0.07);
|
$border: 1px solid mat-color($foreground, divider, 0.07);
|
||||||
|
|
||||||
.sidenav {
|
.aca-menu-panel {
|
||||||
@include angular-material-theme($theme);
|
.action-button--active {
|
||||||
|
color: mat-color($accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
color: mat-color($primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button:hover {
|
||||||
|
color: mat-color($accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidenav {
|
||||||
background-color: mat-color($background, background);
|
background-color: mat-color($background, background);
|
||||||
|
|
||||||
|
.item:hover .action-button__label {
|
||||||
|
color: mat-color($accent);
|
||||||
|
}
|
||||||
|
|
||||||
.mat-expansion-panel {
|
.mat-expansion-panel {
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
color: mat-color($primary, 0.87) !important;
|
color: mat-color($primary, 0.87) !important;
|
||||||
@@ -29,28 +45,20 @@
|
|||||||
font-size: 14px !important;
|
font-size: 14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.adf-sidebar-action-menu-button {
|
.mat-expansion-panel-header-title > span {
|
||||||
background-color: mat-color($accent);
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button--active {
|
||||||
|
color: mat-color($accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
color: mat-color($primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
border-bottom: $border;
|
border-bottom: $border;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item--label:not(.item--active):hover {
|
|
||||||
color: mat-color($foreground, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--label {
|
|
||||||
color: mat-color($primary, 0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--active {
|
|
||||||
color: mat-color($accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item--default {
|
|
||||||
color: mat-color($primary, 0.87);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/app/components/sidenav/sidenav.component.ts
Normal file → Executable file
14
src/app/components/sidenav/sidenav.component.ts
Normal file → Executable file
@@ -25,11 +25,15 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
|
ContentChild,
|
||||||
Input,
|
Input,
|
||||||
|
TemplateRef,
|
||||||
OnInit,
|
OnInit,
|
||||||
ViewEncapsulation,
|
ViewEncapsulation,
|
||||||
OnDestroy
|
OnDestroy
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { CollapsedTemplateDirective } from './directives/collapsed-template.directive';
|
||||||
|
import { ExpandedTemplateDirective } from './directives/expanded-template.directive';
|
||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
import { NavBarGroupRef } from '@alfresco/adf-extensions';
|
import { NavBarGroupRef } from '@alfresco/adf-extensions';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@@ -46,12 +50,16 @@ import { takeUntil, distinctUntilChanged, map } from 'rxjs/operators';
|
|||||||
host: { class: 'app-sidenav' }
|
host: { class: 'app-sidenav' }
|
||||||
})
|
})
|
||||||
export class SidenavComponent implements OnInit, OnDestroy {
|
export class SidenavComponent implements OnInit, OnDestroy {
|
||||||
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
@Input() mode: 'collapsed' | 'expanded' = 'expanded';
|
||||||
|
|
||||||
@Input()
|
@ContentChild(ExpandedTemplateDirective, { read: TemplateRef })
|
||||||
showLabel: boolean;
|
expandedTemplate;
|
||||||
|
|
||||||
|
@ContentChild(CollapsedTemplateDirective, { read: TemplateRef })
|
||||||
|
collapsedTemplate;
|
||||||
|
|
||||||
groups: Array<NavBarGroupRef> = [];
|
groups: Array<NavBarGroupRef> = [];
|
||||||
|
private onDestroy$: Subject<boolean> = new Subject<boolean>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<AppStore>,
|
private store: Store<AppStore>,
|
||||||
|
@@ -26,11 +26,17 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { AppCreateMenuModule } from '../create-menu/create-menu.module';
|
import { AppCreateMenuModule } from '../create-menu/create-menu.module';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SidenavComponent } from './sidenav.component';
|
|
||||||
import { CoreModule } from '@alfresco/adf-core';
|
import { CoreModule } from '@alfresco/adf-core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { AcaExpansionPanelDirective } from './expansion-panel.directive';
|
import { ExpansionPanelDirective } from './directives/expansion-panel.directive';
|
||||||
|
import { MenuPanelDirective } from './directives/menu-panel.directive';
|
||||||
|
import { CollapsedTemplateDirective } from './directives/collapsed-template.directive';
|
||||||
|
import { ExpandedTemplateDirective } from './directives/expanded-template.directive';
|
||||||
|
import { SidenavComponent } from './sidenav.component';
|
||||||
|
import { ActiveLinkDirective } from './directives/active-link.directive';
|
||||||
|
import { ExpandMenuComponent } from './components/expand-menu.component';
|
||||||
|
import { ButtonMenuComponent } from './components/button-menu.component';
|
||||||
|
import { ActionDirective } from './directives/action.directive';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -38,7 +44,27 @@ import { AcaExpansionPanelDirective } from './expansion-panel.directive';
|
|||||||
RouterModule,
|
RouterModule,
|
||||||
AppCreateMenuModule
|
AppCreateMenuModule
|
||||||
],
|
],
|
||||||
declarations: [SidenavComponent, AcaExpansionPanelDirective],
|
declarations: [
|
||||||
exports: [SidenavComponent, AcaExpansionPanelDirective]
|
MenuPanelDirective,
|
||||||
|
ExpansionPanelDirective,
|
||||||
|
ExpandedTemplateDirective,
|
||||||
|
CollapsedTemplateDirective,
|
||||||
|
ActiveLinkDirective,
|
||||||
|
ActionDirective,
|
||||||
|
ExpandMenuComponent,
|
||||||
|
ButtonMenuComponent,
|
||||||
|
SidenavComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
MenuPanelDirective,
|
||||||
|
ExpansionPanelDirective,
|
||||||
|
ExpandedTemplateDirective,
|
||||||
|
CollapsedTemplateDirective,
|
||||||
|
ActiveLinkDirective,
|
||||||
|
ActionDirective,
|
||||||
|
ExpandMenuComponent,
|
||||||
|
ButtonMenuComponent,
|
||||||
|
SidenavComponent
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class AppSidenavModule {}
|
export class AppSidenavModule {}
|
||||||
|
@@ -244,6 +244,7 @@ export class AppExtensionService implements RuleContext {
|
|||||||
.filter(child => this.filterVisible(child))
|
.filter(child => this.filterVisible(child))
|
||||||
.sort(sortByOrder)
|
.sort(sortByOrder)
|
||||||
.map(child => {
|
.map(child => {
|
||||||
|
if (!child.click) {
|
||||||
const childRouteRef = this.extensions.getRouteById(
|
const childRouteRef = this.extensions.getRouteById(
|
||||||
child.route
|
child.route
|
||||||
);
|
);
|
||||||
@@ -254,6 +255,12 @@ export class AppExtensionService implements RuleContext {
|
|||||||
...child,
|
...child,
|
||||||
url: childUrl
|
url: childUrl
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...child,
|
||||||
|
action: child.click
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -261,12 +268,19 @@ export class AppExtensionService implements RuleContext {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!item.click) {
|
||||||
const routeRef = this.extensions.getRouteById(item.route);
|
const routeRef = this.extensions.getRouteById(item.route);
|
||||||
const url = `/${routeRef ? routeRef.path : item.route}`;
|
const url = `/${routeRef ? routeRef.path : item.route}`;
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
url
|
url
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
action: item.click
|
||||||
|
};
|
||||||
})
|
})
|
||||||
.reduce(reduceEmptyMenus, [])
|
.reduce(reduceEmptyMenus, [])
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user