ADF-2974] New Buttons Menu component version (#3429)

* [ADF-2974] Buttons injected from parent html component

* [ADF-2974] New version of buttons menu component

* [ADF-2974] Updated unit tests

* [ADF-2974] Updated documentation

* [ADF-2974] Removed unused variable

* [ADF-2974] Fixed failing test at analytics report parameters

* [ADF-2974] Removed fdescribe

* [ADF-2974] Moved mock inside testing file for buttons menu component
This commit is contained in:
davidcanonieto
2018-06-06 23:07:54 +01:00
committed by Eugenio Romano
parent 1838818295
commit 3759a7967c
11 changed files with 226 additions and 236 deletions

View File

@@ -1,27 +1,20 @@
<div fxShow fxHide.xs="true" *ngIf="hasButtons()" id="adf-buttons-menu">
<ng-container *ngFor="let button of buttons">
<ng-container *ngTemplateOutlet="desktopMenu; context: button"></ng-container>
</ng-container>
</div>
<div fxHide fxShow.xs="true" *ngIf="hasButtons()" id="adf-buttons-menu">
<button mat-icon-button [matMenuTriggerFor]="editReportMenu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #editReportMenu="matMenu">
<ng-container *ngFor="let button of buttons">
<ng-container *ngTemplateOutlet="mobileMenu; context:button"></ng-container>
</ng-container>
</mat-menu>
<div id="adf-buttons-menu" class="adf-buttons-menu" *ngIf="!isMenuEmpty">
<div *ngIf="isMobile()">
<button mat-icon-button [matMenuTriggerFor]="editReportMenu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #editReportMenu="matMenu" class="adf-buttons-menu-mobile">
<ng-content *ngTemplateOutlet="desktop">
</ng-content>
</mat-menu>
</div>
<div *ngIf="!isMobile()" class="adf-buttons-menu-desktop">
<ng-content *ngTemplateOutlet="desktop">
</ng-content>
</div>
</div>
<ng-template #desktopMenu let-handler="handler" let-icon="icon" let-label="label" let-styles="styles" let-id="id" let-isVisible="isVisible">
<button mat-button (click)="handler()" id="{{id}}" *ngIf="isVisible()" class="styles">
<mat-icon>{{icon}}</mat-icon>
</button>
</ng-template>
<ng-template #mobileMenu let-handler="handler" let-icon="icon" let-label="label" let-styles="styles" let-id="id" let-isVisible="isVisible">
<button mat-menu-item (click)="handler()" id="{{id}}" *ngIf="isVisible()" class="styles">
<mat-icon>{{icon}}</mat-icon><span>{{label | translate}}</span>
</button>
</ng-template>
<ng-template #desktop>
<ng-content></ng-content>
</ng-template>

View File

@@ -0,0 +1,32 @@
@mixin adf-buttons-menu-theme($theme) {
.adf-buttons-menu {
margin-right: 10px;
& div {
display: flex;
}
&-mobile {
margin-right: 10px;
}
&-desktop {
display: flex;
button {
color: black;
padding: 0;
}
button > span {
display: none;
}
button > mat-icon.mat-icon.material-icons {
color: black;
margin: 0 10px;
}
}
}
}

View File

@@ -16,76 +16,125 @@
*/
import { TestBed, async } from '@angular/core/testing';
import { ButtonsMenuComponent } from './buttons-menu.component';
import { MenuButton } from './menu-button.model';
import { MaterialModule } from '../material.module';
import { CoreTestingModule } from '../testing/core.testing.module';
import { setupTestBed } from '../testing/setupTestBed';
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
@Component({
selector: 'adf-custom-container',
template: `
<adf-buttons-action-menu>
<button mat-menu-item (click)="assignValue()">
<mat-icon>settings</mat-icon><span> Button </span>
</button>
</adf-buttons-action-menu>
`
})
export class CustomContainerComponent {
value: number;
assignValue() {
this.value = 1;
}
}
@Component({
selector: 'adf-custom-empty-container',
template: `
<adf-buttons-action-menu>
</adf-buttons-action-menu>
`
})
export class CustomEmptyContainerComponent {
}
describe('ButtonsMenuComponent', () => {
let fixture;
let buttonsMenuComponent: ButtonsMenuComponent;
let element: HTMLElement;
describe('When Buttons are injected', () => {
setupTestBed({
imports: [
CoreTestingModule,
MaterialModule
]
});
let fixture;
let component: CustomContainerComponent;
let element: HTMLElement;
beforeEach(() => {
fixture = TestBed.createComponent(ButtonsMenuComponent);
element = fixture.nativeElement;
buttonsMenuComponent = <ButtonsMenuComponent> fixture.debugElement.componentInstance;
});
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
it('should hide buttons menu div if buttons input is empty', async(() => {
buttonsMenuComponent.buttons = [];
fixture.detectChanges();
fixture.whenStable().then(() => {
const buttonsMenuElement = element.querySelector('#adf-buttons-menu');
expect(buttonsMenuElement).toBeNull();
setupTestBed({
imports: [
CoreTestingModule,
MaterialModule
],
declarations: [
CustomContainerComponent
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
});
}));
it('should render buttons menu when there is at least one button declared in the buttons array', async(() => {
const button = new MenuButton({
label: 'button',
icon: 'button',
id: 'clickMe'
beforeEach(() => {
fixture = TestBed.createComponent(CustomContainerComponent);
element = fixture.debugElement.nativeElement;
component = fixture.componentInstance;
});
buttonsMenuComponent.buttons = [button];
fixture.detectChanges();
fixture.whenStable().then(() => {
const buttonsMenuElement = element.querySelector('#adf-buttons-menu');
expect(buttonsMenuElement).not.toBeNull();
expect(buttonsMenuElement).toBeDefined();
});
}));
it('should call the handler function when button is clicked', async(() => {
const button = new MenuButton({
label: 'button',
icon: 'button',
id: 'clickMe'
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
button.handler = jasmine.createSpy('handler');
buttonsMenuComponent.buttons = [button];
fixture.detectChanges();
fixture.whenStable().then(() => {
const buttonsMenuElement: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#clickMe');
expect(buttonsMenuElement).not.toBeNull();
expect(buttonsMenuElement).toBeDefined();
buttonsMenuElement.click();
it('should render buttons menu when at least one button is declared', async(() => {
fixture.detectChanges();
expect(button.handler).toHaveBeenCalled();
fixture.whenStable().then(() => {
const buttonsMenuElement = element.querySelector('#adf-buttons-menu');
expect(buttonsMenuElement).toBeDefined();
});
}));
it('should trigger event when a specific button is clicked', async(() => {
expect(component.value).toBeUndefined();
let button = element.querySelector('button');
button.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.value).toBe(1);
});
}));
});
describe('When no buttons are injected', () => {
let fixture;
let element: HTMLElement;
setupTestBed({
imports: [
CoreTestingModule,
MaterialModule
],
declarations: [
CustomEmptyContainerComponent
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
});
}));
beforeEach(() => {
fixture = TestBed.createComponent(CustomEmptyContainerComponent);
element = fixture.nativeElement;
});
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
it('should hide buttons menu if buttons input is empty', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const buttonsMenuElement = element.querySelector('#adf-buttons-menu');
expect(buttonsMenuElement).toBeNull();
});
}));
});
});

View File

@@ -15,25 +15,30 @@
* limitations under the License.
*/
/* tslint:disable:component-selector no-access-missing-member no-input-rename */
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { MenuButton } from './menu-button.model';
import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { MatMenuItem } from '@angular/material';
@Component({
selector: 'adf-buttons-action-menu',
templateUrl: './buttons-menu.component.html'
templateUrl: './buttons-menu.component.html',
styleUrls: ['./buttons-menu.component.scss']
})
export class ButtonsMenuComponent implements OnChanges {
/** Array of buttons that defines the menu. */
@Input() buttons: MenuButton[];
export class ButtonsMenuComponent implements AfterContentInit {
ngOnChanges(changes: SimpleChanges) {
this.buttons = changes['buttons'].currentValue;
@ContentChildren(MatMenuItem) buttons: QueryList<MatMenuItem>;
isMenuEmpty: boolean;
ngAfterContentInit() {
if (this.buttons.length > 0) {
this.isMenuEmpty = false;
} else {
this.isMenuEmpty = true;
}
}
hasButtons() {
return this.buttons.length > 0 ? true : false;
isMobile(): boolean {
return !!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
}

View File

@@ -1,37 +0,0 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type VisibiltyFunction = (obj?: any) => boolean;
const defaultValidation = () => true;
export class MenuButton {
label: string;
icon: string;
handler: any;
styles: string;
id: string;
isVisible: VisibiltyFunction;
constructor(obj?: any) {
this.label = obj.label;
this.icon = obj.icon;
this.handler = obj.handler;
this.styles = obj.styles || null;
this.id = obj.id || null;
this.isVisible = obj.isVisible ? obj.isVisible : defaultValidation;
}
}

View File

@@ -16,5 +16,4 @@
*/
export * from './buttons-menu.component';
export * from './menu-button.model';
export * from './buttons-menu.module';

View File

@@ -28,6 +28,7 @@
@import '../sidenav-layout/components/layout-container/layout-container.component';
@import "../templates/empty-content/empty-content.component";
@import "../templates/error-content/error-content.component";
@import "../buttons-menu/buttons-menu.component";
@mixin adf-core-theme($theme) {
@include adf-colors-theme($theme);
@@ -58,6 +59,7 @@
@include adf-layout-container-theme($theme);
@include adf-empty-content-theme($theme);
@include adf-error-content-theme($theme);
@include adf-buttons-menu-theme($theme);
}