From 3759a7967cd29bf81349c0a04997055412fd4877 Mon Sep 17 00:00:00 2001 From: davidcanonieto Date: Wed, 6 Jun 2018 23:07:54 +0100 Subject: [PATCH] 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 --- docs/core/buttons-menu.component.md | 83 +++------ .../buttons-menu/buttons-menu.component.html | 43 ++--- .../buttons-menu/buttons-menu.component.scss | 32 ++++ .../buttons-menu.component.spec.ts | 163 ++++++++++++------ .../buttons-menu/buttons-menu.component.ts | 29 ++-- lib/core/buttons-menu/menu-button.model.ts | 37 ---- lib/core/buttons-menu/public-api.ts | 1 - lib/core/styles/_index.scss | 2 + ...analytics-report-parameters.component.html | 17 +- ...lytics-report-parameters.component.spec.ts | 20 +-- .../analytics-report-parameters.component.ts | 35 +--- 11 files changed, 226 insertions(+), 236 deletions(-) create mode 100644 lib/core/buttons-menu/buttons-menu.component.scss delete mode 100644 lib/core/buttons-menu/menu-button.model.ts diff --git a/docs/core/buttons-menu.component.md b/docs/core/buttons-menu.component.md index fc3ace5dce..214e52d273 100644 --- a/docs/core/buttons-menu.component.md +++ b/docs/core/buttons-menu.component.md @@ -6,76 +6,47 @@ Last reviewed: 2018-04-24 # Buttons Menu Component -Displays buttons on a responsive menu. - -![adf-buttons-menu-desktop](../docassets/images/adf-buttons-menu-desktop.png) +Displays buttons on a responsive menu. This way the html doesn't need to ## Basic Usage +In order to use this component, you will have to place the buttons that you want to have in your menu inside this component's html tags. +They must use the following structure: + ```html - + + + ``` +Notice that they need an icon and a label for the button inside a span tag. They also make use of the Angular material directive `mat-menu-item`. + +```html + +``` + ## Class members ### Properties -| Name | Type | Default value | Description | -| -- | -- | -- | -- | -| buttons | [`MenuButton`](../../lib/core/buttons-menu/menu-button.model.ts)`[]` | | Array of buttons that defines the menu. | - +| Name | Type | Description | +| -- | -- | -- | +| isMenuEmpty | boolean | If the menu has no buttons it won't be displayed. | + ## Details -This component shows buttons on a responsive menu. The display of the menu changes to fit -the screen size of the device: - -Desktop view of the menu +This component is fully responsive and it will display two different layouts regarding the screen size. +#### Desktop View ![adf-buttons-menu-desktop](../docassets/images/adf-buttons-menu-desktop.png) - -Mobile view of the menu - +#### Mobile View ![adf-buttons-menu-mobile](../docassets/images/adf-buttons-menu-mobile.png) -The `buttons` property contains an array of [`MenuButton`](../../lib/core/buttons-menu/menu-button.model.ts) instances that define -the label and appearance of each button along with a handler function to -implement its action: - -```ts -buttons: MenuButton[] = []; - - setButtons() { - this.buttons = [ - new MenuButton({ - label: 'Settings', - icon: 'settings', - handler: this.settings.bind(this) - }), - new MenuButton({ - label: 'Delete', - icon: 'delete', - handler: this.deleteItem.bind(this, this.reportId), - id: 'delete-button' - }), - new MenuButton({ - label: 'Export', - icon: 'file_download', - handler: this.exportItem.bind(this), - id: 'export-button', - isVisible: this.isItemValid.bind(this) - }), - new MenuButton({ - label: 'Save', - icon: 'save', - handler: this.saveItem.bind(this), - id: 'save-button', - isVisible: this.isItemValid.bind(this) - }) - ]; -``` - -## See also - -- [Menu Button Model](./menu-button.model.md) diff --git a/lib/core/buttons-menu/buttons-menu.component.html b/lib/core/buttons-menu/buttons-menu.component.html index ce3030d75b..6edbf0a2f4 100644 --- a/lib/core/buttons-menu/buttons-menu.component.html +++ b/lib/core/buttons-menu/buttons-menu.component.html @@ -1,27 +1,20 @@ -
- - - -
-
- - - - - - +
+
+ + + + + +
+ +
+ + +
- - - - - - - + + + \ No newline at end of file diff --git a/lib/core/buttons-menu/buttons-menu.component.scss b/lib/core/buttons-menu/buttons-menu.component.scss new file mode 100644 index 0000000000..00719b20b0 --- /dev/null +++ b/lib/core/buttons-menu/buttons-menu.component.scss @@ -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; + } + } + } +} \ No newline at end of file diff --git a/lib/core/buttons-menu/buttons-menu.component.spec.ts b/lib/core/buttons-menu/buttons-menu.component.spec.ts index 28b9e00c24..da3cb27186 100644 --- a/lib/core/buttons-menu/buttons-menu.component.spec.ts +++ b/lib/core/buttons-menu/buttons-menu.component.spec.ts @@ -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: ` + + + + ` +}) +export class CustomContainerComponent { + + value: number; + + assignValue() { + this.value = 1; + } +} + +@Component({ + selector: 'adf-custom-empty-container', + template: ` + + + ` +}) +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 = 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 = 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(); + }); + })); + }); + }); diff --git a/lib/core/buttons-menu/buttons-menu.component.ts b/lib/core/buttons-menu/buttons-menu.component.ts index 6f7f184fca..beec8ffd41 100644 --- a/lib/core/buttons-menu/buttons-menu.component.ts +++ b/lib/core/buttons-menu/buttons-menu.component.ts @@ -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; + + 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); } } diff --git a/lib/core/buttons-menu/menu-button.model.ts b/lib/core/buttons-menu/menu-button.model.ts deleted file mode 100644 index be28776fd8..0000000000 --- a/lib/core/buttons-menu/menu-button.model.ts +++ /dev/null @@ -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; - } -} diff --git a/lib/core/buttons-menu/public-api.ts b/lib/core/buttons-menu/public-api.ts index e50687749e..6556b13d93 100644 --- a/lib/core/buttons-menu/public-api.ts +++ b/lib/core/buttons-menu/public-api.ts @@ -16,5 +16,4 @@ */ export * from './buttons-menu.component'; -export * from './menu-button.model'; export * from './buttons-menu.module'; diff --git a/lib/core/styles/_index.scss b/lib/core/styles/_index.scss index 48b2d2c9b5..98dfbb42b7 100644 --- a/lib/core/styles/_index.scss +++ b/lib/core/styles/_index.scss @@ -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); } diff --git a/lib/insights/analytics-process/components/analytics-report-parameters.component.html b/lib/insights/analytics-process/components/analytics-report-parameters.component.html index b3182455ad..0649e801a2 100644 --- a/lib/insights/analytics-process/components/analytics-report-parameters.component.html +++ b/lib/insights/analytics-process/components/analytics-report-parameters.component.html @@ -25,8 +25,21 @@

{{reportParameters.name}}

- + + + +
+ + +
{ it('Should raise an event for report deleted', async(() => { fixture.detectChanges(); - fixture.whenStable().then(() => { - let deleteButton = fixture.debugElement.nativeElement.querySelector('#delete-button'); - expect(deleteButton).toBeDefined(); - expect(deleteButton).not.toBeNull(); - component.deleteReportSuccess.subscribe((reportId) => { - expect(reportId).not.toBeNull(); - }); - deleteButton.click(); - jasmine.Ajax.requests.mostRecent().respondWith({ - status: 200, - contentType: 'json' - }); + spyOn(component, 'deleteReport'); + let deleteButton = fixture.debugElement.nativeElement.querySelector('#delete-button'); + expect(deleteButton).toBeDefined(); + expect(deleteButton).not.toBeNull(); + component.deleteReportSuccess.subscribe((reportId) => { + expect(reportId).not.toBeNull(); }); + deleteButton.click(); + expect(component.deleteReport).toHaveBeenCalled(); })); it('Should hide export button if the form is not valid', async(() => { diff --git a/lib/insights/analytics-process/components/analytics-report-parameters.component.ts b/lib/insights/analytics-process/components/analytics-report-parameters.component.ts index 4a8a61dbff..ad7da8a374 100644 --- a/lib/insights/analytics-process/components/analytics-report-parameters.component.ts +++ b/lib/insights/analytics-process/components/analytics-report-parameters.component.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ContentService, LogService, MenuButton } from '@alfresco/adf-core'; +import { ContentService, LogService } from '@alfresco/adf-core'; import { AfterContentChecked, Component, @@ -95,8 +95,6 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On reportName: string; - buttons: MenuButton[] = []; - private dropDownSub; private reportParamsSub; private paramOpts; @@ -138,7 +136,6 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On if (reportId && reportId.currentValue) { this.reportId = reportId.currentValue; this.getReportParams(reportId.currentValue); - this.setButtons(); } let appId = changes['appId']; @@ -391,34 +388,4 @@ export class AnalyticsReportParametersComponent implements OnInit, OnChanges, On isFormValid() { return this.reportForm && this.reportForm.dirty && this.reportForm.valid; } - - setButtons() { - this.buttons = [ - new MenuButton({ - label: 'ANALYTICS.MESSAGES.ICON-SETTING', - icon: 'settings', - handler: this.toggleParameters.bind(this) - }), - new MenuButton({ - label: 'ANALYTICS.MESSAGES.ICON-DELETE', - icon: 'delete', - handler: this.deleteReport.bind(this, this.reportId), - id: 'delete-button' - }), - new MenuButton({ - label: 'ANALYTICS.MESSAGES.ICON-EXPORT-CSV', - icon: 'file_download', - handler: this.showDialog.bind(this, 'Export'), - id: 'export-button', - isVisible: this.isFormValid.bind(this) - }), - new MenuButton({ - label: 'ANALYTICS.MESSAGES.ICON-SAVE', - icon: 'save', - handler: this.showDialog.bind(this, 'Save'), - id: 'save-button', - isVisible: this.isFormValid.bind(this) - }) - ]; - } }