Skip to content

Commit

Permalink
feat(Popover): implementa la direttiva popover
Browse files Browse the repository at this point in the history
ref #23
  • Loading branch information
Mario Traetta authored and ciccio86 committed Aug 31, 2018
1 parent 10b7470 commit 717801e
Show file tree
Hide file tree
Showing 54 changed files with 2,100 additions and 1 deletion.
107 changes: 107 additions & 0 deletions e2e/src/popover/popover.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { browser, ElementFinder } from 'protractor';
import { PopoverPage } from './popover.po';
import { timeout } from 'q';

describe('Popover', () => {
let page: PopoverPage;

beforeEach(async() => {
page = new PopoverPage();
await page.go();
});

it('dovrebbe iniziare creato e visualizzato', async () => {
expect(await page.existsStandardExamplePopover()).toBeFalsy();

await page.clickOverStandardExample();
expect(await page.existsStandardExamplePopover()).toBeTruthy();
expect(await page.getStandardExamplePopoverClass()).toBe('popover bs-popover-right');

await page.clickOverStandardExample();
expect(await page.existsStandardExamplePopover()).toBeTruthy();
expect(await page.getStandardExamplePopoverClass()).toBe('popover bs-popover-right');
});

it('dovrebbe iniziare creato e visualizzato in alto', async () => {
expect(await page.existsStandardExamplePopover()).toBeFalsy();

await page.clickOverTopPlacement();
await page.clickOverStandardExample();

expect(await page.existsStandardExamplePopover()).toBeTruthy();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-top');
});

it('dovrebbe iniziare creato e visualizzato a destra', async () => {
expect(await page.existsStandardExamplePopover()).toBeFalsy();

await page.clickOverRightPlacement();
await page.clickOverStandardExample();

expect(await page.existsStandardExamplePopover()).toBeTruthy();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-right');
});

it('dovrebbe iniziare creato e visualizzato a sinistra', async () => {
expect(await page.existsStandardExamplePopover()).toBeFalsy();

await page.clickOverLeftPlacement();
await page.clickOverStandardExample();

expect(await page.existsStandardExamplePopover()).toBeTruthy();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-left');
});

it('dovrebbe iniziare creato e visualizzato in basso', async () => {
expect(await page.existsStandardExamplePopover()).toBeFalsy();

await page.clickOverBottomPlacement();
await page.clickOverStandardExample();

expect(await page.existsStandardExamplePopover()).toBeTruthy();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-bottom');
});

it('dovrebbe iniziare creato e visualizzato a destra, poi in alto e successivamente a sinistra, ed infine in basso', async () => {
await page.clickOverStandardExample();
expect(await page.existsPlacementExamplePopover()).toBeTruthy();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-right');

await page.clickOverTopPlacement();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-top');

await page.clickOverLeftPlacement();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-left');

await page.clickOverBottomPlacement();
expect(await page.getPlacementExamplePopoverClass()).toBe('popover bs-popover-bottom');
});

it('dovrebbe iniziare creato e visualizzato, per poi essere chiuso con un click successivo', async () => {
await page.clickOverFocusTrigger();

await page.clickOverStandardExample();
const isPresent = await page.existsFocusExamplePopover();
expect(isPresent).toBeTruthy();

await page.clickOverFocusTrigger();

const isHidden = await page.getFocusExamplePopoverClass();
expect(isHidden).toBeTruthy();
});

it('dovrebbe iniziare creato e visualizzato, per poi essere distrutto ed infine ricreato e rivisualizzato', async () => {
await page.clickOverStandardExample();
const isPresent = await page.existsDisposableExamplePopover();
expect(isPresent).toBeTruthy();

await page.clickOverDisposeButton();

const isDisposed = await page.existsDisposableExamplePopover();
expect(isDisposed).toBeFalsy();

await page.clickOverStandardExample();
expect(await page.existsDisposableExamplePopover()).toBeTruthy();
});

});
94 changes: 94 additions & 0 deletions e2e/src/popover/popover.po.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { browser, by, element } from 'protractor';

export class PopoverPage {
private readonly POPOVER_URL = '/#/componenti/popover';
private readonly ID_EXAMPLE_TAB = 'popover-examples-tab';

private readonly ID_POPOVER_INTERACTIVE_BUTTON = 'popover-interactive-button';
private readonly ID_POPOVER_DISPOSE_BUTTON = 'popover-disposing-button';

private readonly ID_RADIO_FOCUS = this.getLabelForAttribute('radio-5');

private readonly ID_RADIO_RIGHT = this.getLabelForAttribute('radio-7');
private readonly ID_RADIO_TOP = this.getLabelForAttribute('radio-8');
private readonly ID_RADIO_LEFT = this.getLabelForAttribute('radio-9');
private readonly ID_RADIO_BOTTOM = this.getLabelForAttribute('radio-10');

private readonly ID_POPOVER_STANDARD = 'it-popover-0';

async go() {
await browser.get(this.POPOVER_URL);
await browser.executeScript(`document.querySelector('header').remove()`);
await element(by.id(this.ID_EXAMPLE_TAB)).click();
return await browser.sleep(500);
}

private getLabelForAttribute(attr: string) {
return `[for="${attr}"]`;
}

async click(elementId: string) {
await element(by.css(elementId)).click();
}

async clickOverStandardExample() {
await element(by.id(this.ID_POPOVER_INTERACTIVE_BUTTON)).click();
}

async clickOverTopPlacement() {
await this.click(this.ID_RADIO_TOP);
}

async clickOverRightPlacement() {
await this.click(this.ID_RADIO_RIGHT);
}

async clickOverBottomPlacement() {
await this.click(this.ID_RADIO_BOTTOM);
}

async clickOverLeftPlacement() {
await this.click(this.ID_RADIO_LEFT);
}

async existsStandardExamplePopover() {
return await element(by.id(this.ID_POPOVER_STANDARD)).isPresent();
}

async getStandardExamplePopoverClass() {
return await element(by.id(this.ID_POPOVER_STANDARD)).getAttribute('class');
}

async existsPlacementExamplePopover() {
return await element(by.id(this.ID_POPOVER_STANDARD)).isPresent();
}

async getPlacementExamplePopoverClass() {
return await element(by.id(this.ID_POPOVER_STANDARD)).getAttribute('class');
}

async clickOverDisposeButton() {
await element(by.id(this.ID_POPOVER_DISPOSE_BUTTON)).click();
}

async existsDisposableExamplePopover() {
return await element(by.id(this.ID_POPOVER_STANDARD)).isPresent();
}

async getDisposableExamplePopoverClass() {
return await element(by.id(this.ID_POPOVER_STANDARD)).getAttribute('class');
}

async clickOverFocusTrigger() {
await this.click(this.ID_RADIO_FOCUS);
}

async existsFocusExamplePopover() {
return await element(by.id(this.ID_POPOVER_STANDARD)).isPresent();
}

async getFocusExamplePopoverClass() {
return await element(by.id(this.ID_POPOVER_STANDARD)).getAttribute('hidden');
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

import { CheckboxComponent } from './checkbox/checkbox.component';
import { PopoverDirective } from './popover/popover.directive';
import { PopoverComponent } from './popover/popover.component';
import { ProgressBarComponent } from './progress-bar/progress-bar.component';
import { RadioButtonComponent, RadioGroupDirective } from './radio/radio.component';
import { ToggleComponent } from './toggle/toggle.component';
Expand Down Expand Up @@ -36,6 +38,8 @@ import { TabComponent } from './tabs/tab.component';
ButtonComponent,
TooltipDirective,
TooltipComponent,
PopoverDirective,
PopoverComponent,
DropdownComponent,
DropdownItemComponent,
DropdownHeaderComponent,
Expand All @@ -54,13 +58,14 @@ import { TabComponent } from './tabs/tab.component';
ProgressBarComponent,
ButtonComponent,
TooltipDirective,
PopoverDirective,
DropdownComponent,
DropdownItemComponent,
DropdownHeaderComponent,
DropdownDividerComponent,
BreadcrumbComponent,
BreadcrumbItemComponent
],
entryComponents: [TooltipComponent]
entryComponents: [TooltipComponent, PopoverComponent]
})
export class DesignAngularKitModule { }
10 changes: 10 additions & 0 deletions projects/design-angular-kit/src/lib/models/InteractionTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export const InteractionTrigger = t.keyof({
hover: null,
});

export const PopoverTrigger = t.keyof({
click: null,
focus: null,
});

const CLICK = 'click';
const FOCUS = 'focus';
const HOVER = 'hover';
Expand All @@ -15,3 +20,8 @@ export const INTERACTION_TRIGGERS = {
FOCUS: FOCUS,
HOVER: HOVER,
};

export const POPOVER_TRIGGERS = {
CLICK: CLICK,
FOCUS: FOCUS,
};
27 changes: 27 additions & 0 deletions projects/design-angular-kit/src/lib/popover/popover.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
:host.bs-popover-top .arrow, :host.bs-popover-bottom .arrow {
left: 50%;
margin-left: -5px;
}

:host.bs-popover-top-left .arrow, :host.bs-popover-bottom-left .arrow {
left: 2em;
}

:host.bs-popover-top-right .arrow, :host.bs-popover-bottom-right .arrow {
left: auto;
right: 2em;
}

:host.bs-popover-left .arrow, :host.bs-popover-right .arrow {
top: 50%;
margin-top: -5px;
}

:host.bs-popover-left-top .arrow, :host.bs-popover-right-top .arrow {
top: 0.7em;
}

:host.bs-popover-left-bottom .arrow, :host.bs-popover-right-bottom .arrow {
top: auto;
bottom: 0.7em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="arrow"></div>
<h3 class="popover-header">{{title}}</h3>
<div class="popover-body">
<ng-content></ng-content>
</div>
81 changes: 81 additions & 0 deletions projects/design-angular-kit/src/lib/popover/popover.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @license
* Copyright Angular ng-bootstrap team All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://raw.githubusercontent.com/ng-bootstrap/ng-bootstrap/master/LICENSE
*/
import {
Component, ChangeDetectionStrategy,
ElementRef, Renderer2, Input, HostBinding,
ChangeDetectorRef, TemplateRef, ViewContainerRef
} from '@angular/core';
import { Placement } from './positioning';
import { ContentRef } from './popup';

@Component({
selector: 'it-popover-window',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './popover.component.html',
styleUrls: ['./popover.component.css']
})
export class PopoverComponent {
@Input() placement: Placement = 'right';

@Input()
get title() { return this._title; }
set title(value: string) {
this._title = value;
this._changeDetectorRef.detectChanges();
}
private _title: string;

@Input() @HostBinding('id') id: string;
@Input() @HostBinding('hidden') hidden = false;

@HostBinding('attr.role') role = 'tooltip';
@HostBinding('class') get myCssClass(): string {
return 'popover bs-popover-' + this.placement.split('-')[0] + ' bs-popover-' + this.placement;
}

constructor(
private _element: ElementRef<HTMLElement>, private _renderer: Renderer2,
private _changeDetectorRef: ChangeDetectorRef, private _viewContainerRef: ViewContainerRef
) {}

applyPlacement(_placement: Placement) {
// Rimuovi le classi della posizione precedente
this._renderer.removeClass(this._element.nativeElement, 'bs-popover-' + this.placement.toString().split('-')[0]);
this._renderer.removeClass(this._element.nativeElement, 'bs-popover-' + this.placement.toString());

// Imposta la nuova posizione
this.placement = _placement;

// Applica le classi della nuova posizione
this._renderer.addClass(this._element.nativeElement, 'bs-popover-' + this.placement.toString().split('-')[0]);
this._renderer.addClass(this._element.nativeElement, 'bs-popover-' + this.placement.toString());
}

setNewContent(content: string | TemplateRef<any>, context?: any) {
const contentElement = this._element.nativeElement.querySelector('.popover-body');

let newContent: ContentRef;
if (!content) {
newContent = new ContentRef([]);
} else if (content instanceof TemplateRef) {
const viewRef = this._viewContainerRef.createEmbeddedView(<TemplateRef<PopoverComponent>>content, context);
newContent = new ContentRef([viewRef.rootNodes], viewRef);
} else {
newContent = new ContentRef([[this._renderer.createText(`${content}`)]]);
}

const childNodes = contentElement.childNodes;
for (let i = 0; i < childNodes.length; i++) {
this._renderer.removeChild(contentElement, childNodes[i]);
}

newContent.nodes.forEach(newNode => {
newNode.forEach(newChild => this._renderer.appendChild(contentElement, newChild));
});
}
}
23 changes: 23 additions & 0 deletions projects/design-angular-kit/src/lib/popover/popover.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @license
* Copyright Angular ng-bootstrap team All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://raw.githubusercontent.com/ng-bootstrap/ng-bootstrap/master/LICENSE
*/
import { Injectable } from '@angular/core';
import { PlacementArray } from './positioning';
import { INTERACTION_TRIGGERS } from '../models/InteractionTrigger';

/**
* Servizio di configurazione per la direttiva ItPopover.
* Il servizio può essere iniettato, tipicamente in un root component, per impostare i valori delle proprietà in
* modo tale da fornire i valori di default per tutti i popover utilizzati in un'applicazione.
*/
@Injectable({providedIn: 'root'})
export class PopoverConfig {
placement: PlacementArray = 'right';
triggers = INTERACTION_TRIGGERS.CLICK;
container: string;
disablePopover = false;
}
Loading

0 comments on commit 717801e

Please sign in to comment.