Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Added message stream to popover #1978

Merged
merged 9 commits into from
Sep 18, 2018
25 changes: 25 additions & 0 deletions src/demos/popover/popover-demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,28 @@ <h3>
<sky-popover #asyncPopover>
My asynchronous popover.
</sky-popover>

<h3>
Popovers may be interacted with programatically

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"programmatically"

</h3>

<p>
<button
type="button"
class="sky-btn sky-btn-default"
[skyPopover]="programaticPopover"
Blackbaud-TrevorBurch marked this conversation as resolved.
Show resolved Hide resolved

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"programmatic"

[skyPopoverMessageStream]="popoverController">
Open popover via click
</button>


<button
type="button"
class="sky-btn sky-btn-default"
(click)="openPopover()">
Open popover programatically

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"programmatically"

</button>
</p>
<sky-popover #programaticPopover>
This popover has a programatic trigger on the second button.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"programmatic"

</sky-popover>
17 changes: 17 additions & 0 deletions src/demos/popover/popover-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
import {
SkyPopoverComponent
} from '../../core';
import { SkyPopoverMessageType } from '../../modules/popover/types/popover-message-type';
import { SkyPopoverMessage } from '../../modules/popover/types/popover-message';
import { Subject } from 'rxjs';

@Component({
selector: 'sky-popover-demo',
Expand All @@ -22,6 +25,8 @@ export class SkyPopoverDemoComponent {
@ViewChild('asyncPopover')
public asyncPopover: SkyPopoverComponent;

public popoverController = new Subject<SkyPopoverMessage>();

constructor() {
setTimeout(() => {
this.asyncPopoverRef = this.asyncPopover;
Expand All @@ -35,4 +40,16 @@ export class SkyPopoverDemoComponent {
public onPopoverClosed(popoverComponent: any) {
alert('The popover was closed: ' + popoverComponent.popoverTitle);
}

public openPopover() {
this.sendMessage(SkyPopoverMessageType.Open);
setTimeout(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is causing some strange behavior in the demo. I would isolate this to two separate buttons, "Open" and "Close".

this.sendMessage(SkyPopoverMessageType.Close);
}, 5000);
}

private sendMessage(type: SkyPopoverMessageType) {
const message: SkyPopoverMessage = { type };
this.popoverController.next(message);
}
}
13 changes: 13 additions & 0 deletions src/modules/popover/fixtures/popover.component.fixture.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,17 @@
<sky-popover #anotherAsyncPopover>
My asynchronous popover.
</sky-popover>

<button
type="button"
class="sky-btn sky-btn-defauilt"
[skyPopover]="programaticPopover"
[skyPopoverMessageStream]="messageStream"
>
Open
</button>

<sky-popover #programaticPopover>
My programatic popover.
</sky-popover>
</div>
18 changes: 17 additions & 1 deletion src/modules/popover/fixtures/popover.component.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@ import {
Component,
ViewChild
} from '@angular/core';
import {
Subject
} from 'rxjs/Subject';

import { SkyPopoverComponent } from '../popover.component';
import {
SkyPopoverComponent
} from '../popover.component';
import {
SkyPopoverMessage,
SkyPopoverMessageType
} from '../types';

@Component({
selector: 'sky-test-component',
templateUrl: './popover.component.fixture.html'
})
export class SkyPopoverTestComponent {

public messageStream = new Subject<SkyPopoverMessage>();
public asyncPopoverRef: SkyPopoverComponent;

@ViewChild('asyncPopover')
Expand All @@ -25,4 +36,9 @@ export class SkyPopoverTestComponent {
public attachAnotherAsyncPopover() {
this.asyncPopoverRef = this.anotherAsyncPopover;
}

public sendMessage(messageType: SkyPopoverMessageType) {
this.messageStream.next({ type: messageType });
}

}
36 changes: 34 additions & 2 deletions src/modules/popover/popover.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {

import {
ComponentFixture,
TestBed
TestBed,
tick,
fakeAsync
} from '@angular/core/testing';

import {
Expand All @@ -31,6 +33,7 @@ import {
} from './index';

import { SkyPopoverTestComponent } from './fixtures/popover.component.fixture';
import { SkyPopoverMessageType } from './types/popover-message-type';

class MockWindowService {
public getWindow(): any {
Expand Down Expand Up @@ -104,7 +107,7 @@ describe('SkyPopoverDirective', () => {
{ provide: SkyWindowRefService, useValue: mockWindowService }
]
})
.compileComponents();
.compileComponents();

fixture = TestBed.createComponent(SkyPopoverTestComponent);
directiveElements = fixture.debugElement.queryAll(By.directive(SkyPopoverDirective));
Expand Down Expand Up @@ -256,4 +259,33 @@ describe('SkyPopoverDirective', () => {
expect(addEventSpy).not.toHaveBeenCalled();
expect(removeEventSpy).toHaveBeenCalled();
});

describe('message stream', () => {
it('should allow opening and closing the menu', fakeAsync(() => {
const caller = directiveElements[5];
const callerInstance = caller.injector.get(SkyPopoverDirective);
const openSpy = spyOn(callerInstance.skyPopover, 'positionNextTo').and.stub();
const closeSpy = spyOn(callerInstance.skyPopover, 'close').and.stub();

fixture.detectChanges();
tick();
fixture.detectChanges();

let component = fixture.componentInstance;
component.sendMessage(SkyPopoverMessageType.Open);
fixture.detectChanges();
tick();
fixture.detectChanges();

expect(openSpy).toHaveBeenCalled();

component.sendMessage(SkyPopoverMessageType.Close);
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(closeSpy).toHaveBeenCalled();

fixture.destroy();
}));
});
});
32 changes: 32 additions & 0 deletions src/modules/popover/popover.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
} from './types';

import { SkyPopoverComponent } from './popover.component';
import { SkyPopoverMessage } from './types/popover-message';
import { SkyPopoverMessageType } from './types/popover-message-type';

@Directive({
selector: '[skyPopover]'
Expand All @@ -40,6 +42,9 @@ export class SkyPopoverDirective implements OnChanges, OnDestroy {
@Input()
public skyPopoverTrigger: SkyPopoverTrigger = 'click';

@Input()
public skyPopoverMessageStream = new Subject<SkyPopoverMessage>();

private idled = new Subject<boolean>();

constructor(
Expand All @@ -59,6 +64,7 @@ export class SkyPopoverDirective implements OnChanges, OnDestroy {

public ngOnDestroy(): void {
this.removeEventListeners();
this.idled.complete();
}

public togglePopover() {
Expand Down Expand Up @@ -97,6 +103,12 @@ export class SkyPopoverDirective implements OnChanges, OnDestroy {
private addEventListeners() {
const element = this.elementRef.nativeElement;

this.skyPopoverMessageStream
.takeUntil(this.idled)
.subscribe(message => {
this.handleIncomingMessages(message);
});

Observable
.fromEvent(element, 'keyup')
.takeUntil(this.idled)
Expand Down Expand Up @@ -159,4 +171,24 @@ export class SkyPopoverDirective implements OnChanges, OnDestroy {
this.idled.unsubscribe();
this.idled = new Subject<boolean>();
}

private handleIncomingMessages(message: SkyPopoverMessage) {
/* tslint:disable-next-line:switch-default */
switch (message.type) {
case SkyPopoverMessageType.Open:
this.positionPopover();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure that the popover itself uses the message stream to open/close/position so that consumers can interrupt the message stream as they see fit.

For example, you would replace this.positionPopover(), here:

this.positionPopover();

...with this.sendMessage(SkyPopoverMessageType.Open);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created: #1994

break;

case SkyPopoverMessageType.Close:
this.closePopover();
break;

case SkyPopoverMessageType.Reposition:
// Only reposition the popover if it is already open.
if (this.isPopoverOpen()) {
this.positionPopover();
}
break;
}
}
}
2 changes: 2 additions & 0 deletions src/modules/popover/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * from './popover-alignment';
export * from './popover-placement';
export * from './popover-trigger';
export * from './popover-position';
export * from './popover-message';
export * from './popover-message-type';
5 changes: 5 additions & 0 deletions src/modules/popover/types/popover-message-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum SkyPopoverMessageType {
Open = 0,
Close = 1,
Reposition = 2
}
5 changes: 5 additions & 0 deletions src/modules/popover/types/popover-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SkyPopoverMessageType } from './popover-message-type';

export interface SkyPopoverMessage {
type?: SkyPopoverMessageType;
}