Skip to content

Commit

Permalink
feat(stark-ui): implementation of progress-indicator component
Browse files Browse the repository at this point in the history
ISSUES CLOSED: #126
  • Loading branch information
Mallikki committed Feb 1, 2019
1 parent 190a468 commit 5240bee
Show file tree
Hide file tree
Showing 40 changed files with 1,651 additions and 4 deletions.
4 changes: 4 additions & 0 deletions packages/stark-ui/assets/styles/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,7 @@ body {
.flex-fill {
flex: 1;
}

.stark-hide {
display: none;
}
1 change: 1 addition & 0 deletions packages/stark-ui/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export * from "./modules/message-pane";
export * from "./modules/minimap";
export * from "./modules/pagination";
export * from "./modules/pretty-print";
export * from "./modules/progress-indicator";
export * from "./modules/route-search";
export * from "./modules/session-ui";
export * from "./modules/slider";
Expand Down
6 changes: 6 additions & 0 deletions packages/stark-ui/src/modules/progress-indicator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from "./progress-indicator/progress-indicator.module";
export * from "./progress-indicator/actions";
export * from "./progress-indicator/directives";
export * from "./progress-indicator/entities";
export * from "./progress-indicator/reducers";
export * from "./progress-indicator/services";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./actions/progress-indicator.actions";
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { StarkProgressIndicatorConfig } from "../entities";
import { Action } from "@ngrx/store";

/**
* Progress indicator action types enumeration
*/
export enum StarkProgressIndicatorActionTypes {
PROGRESS_INDICATOR_REGISTER = "PROGRESS_INDICATOR_REGISTER",
PROGRESS_INDICATOR_DEREGISTER = "PROGRESS_INDICATOR_DEREGISTER",
PROGRESS_INDICATOR_HIDE = "PROGRESS_INDICATOR_HIDE",
PROGRESS_INDICATOR_SHOW = "PROGRESS_INDICATOR_SHOW"
}

export class StarkProgressIndicatorRegister implements Action {
public payload: StarkProgressIndicatorConfig;
/**
* The type of action
* @link StarkProgressIndicatorActionTypes
*/
public readonly type: StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_REGISTER =
StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_REGISTER;

/**
* Class constructor
* @param starkProgressIndicatorConfig - configuration of the indicator
*/
public constructor(starkProgressIndicatorConfig: StarkProgressIndicatorConfig) {
this.payload = starkProgressIndicatorConfig;
}
}

export class StarkProgressIndicatorDeregister implements Action {
public payload: string;

/**
* The type of action
* @link StarkProgressIndicatorActionTypes
*/
public readonly type: StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_DEREGISTER =
StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_DEREGISTER;

/**
* Class constructor
* @param topic - the topic of the indicator
*/
public constructor(topic: string) {
this.payload = topic;
}
}

export class StarkProgressIndicatorHide implements Action {
public payload: string;

/**
* The type of action
* @link StarkProgressIndicatorActionTypes
*/
public readonly type: StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_HIDE =
StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_HIDE;

/**
* Class constructor
* @param topic - the topic of the indicator
*/
public constructor(topic: string) {
this.payload = topic;
}
}

export class StarkProgressIndicatorShow implements Action {
public payload: string;
/**
* The type of action
* @link StarkProgressIndicatorActionTypes
*/
public readonly type: StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_SHOW =
StarkProgressIndicatorActionTypes.PROGRESS_INDICATOR_SHOW;

/**
* Class constructor
* @param topic - the topic of the indicator
*/
public constructor(topic: string) {
this.payload = topic;
}
}

export type StarkProgressIndicatorActions =
| StarkProgressIndicatorRegister
| StarkProgressIndicatorDeregister
| StarkProgressIndicatorHide
| StarkProgressIndicatorShow;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div class="stark-loading-icon" *ngIf="isShown"></div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// tslint:disable:completed-docs
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { STARK_LOGGING_SERVICE } from "@nationalbankbelgium/stark-core";
import { MockStarkLoggingService } from "@nationalbankbelgium/stark-core/testing";
import { StarkProgressIndicatorComponent } from "./progress-indicator.component";
import { By } from "@angular/platform-browser";

/* tslint:disable:no-big-function */
describe("ProgressIndicatorComponent", () => {
let component: StarkProgressIndicatorComponent;
let hostFixture: ComponentFixture<StarkProgressIndicatorComponent>;

beforeEach(async(() => {
return TestBed.configureTestingModule({
declarations: [StarkProgressIndicatorComponent],
providers: [{ provide: STARK_LOGGING_SERVICE, useValue: new MockStarkLoggingService() }]
}).compileComponents();
}));

beforeEach(() => {
hostFixture = TestBed.createComponent(StarkProgressIndicatorComponent);
component = hostFixture.componentInstance;
hostFixture.detectChanges();
});

describe("on initialization", () => {
it("should set internal component properties", () => {
expect(hostFixture).toBeDefined();
expect(component).toBeDefined();

expect(component.logger).not.toBeNull();
expect(component.logger).toBeDefined();
});
});

describe("display Progress Indicator", () => {
it("should not display the progress indicator if isShown is set to false", () => {
component.isShown = false;
expect(hostFixture.debugElement.query(By.css(".div"))).toBeNull();
});

it("should display the progress indicator if isShown is set to true", () => {
component.isShown = true;
expect(hostFixture.debugElement.query(By.css(".div"))).toBeDefined();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Component, ElementRef, Inject, OnInit, Renderer2, ViewEncapsulation } from "@angular/core";

import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core";
import { AbstractStarkUiComponent } from "../../../common/classes/abstract-component";

/**
* Name of the component
*/
const componentName: string = "stark-progress-indicator";

/**
* Component that is dynamically created by the ProgressIndicatorDirective
*/
@Component({
selector: "stark-progress-indicator",
templateUrl: "./progress-indicator.component.html",
encapsulation: ViewEncapsulation.None,
// We need to use host instead of @HostBinding: https://github.com/NationalBankBelgium/stark/issues/664
host: {
class: componentName
}
})
export class StarkProgressIndicatorComponent extends AbstractStarkUiComponent implements OnInit {
public isShown: boolean = false;

public constructor(
@Inject(STARK_LOGGING_SERVICE) public logger: StarkLoggingService,
protected renderer: Renderer2,
protected elementRef: ElementRef
) {
super(renderer, elementRef);
}

/**
* Component lifecycle hook
*/
public ngOnInit(): void {
this.logger.debug(componentName + ": controller initialized");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./directives/progress-indicator.directive";
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
ComponentFactory,
ComponentFactoryResolver,
ComponentRef,
Directive,
ElementRef,
Inject,
Input,
OnDestroy,
OnInit,
Renderer2,
ViewContainerRef
} from "@angular/core";
import { STARK_PROGRESS_INDICATOR_SERVICE, StarkProgressIndicatorService } from "../services";
import { StarkProgressIndicatorConfig, StarkProgressIndicatorType } from "../entities";
import { Subscription } from "rxjs";
import { StarkProgressIndicatorComponent } from "../components/progress-indicator.component";

/**
* Name of the directive
*/
const directiveName: string = "[starkProgressIndicator]";

/**
* This directive must be used as attribute on a DOM element and the config provided should be a {@link StarkProgressIndicatorConfig} object:
*
* ```html
* <some-element [starkProgressIndicator]="{topic: 'some-topic', type: 'SPINNER'}" ></some-element>
* <!-- or -->
* <some-element [starkProgressIndicator]="progressIndicatorConfig" ></some-element>
* ```
*
* Once the directive is initialized, it registers itself to the {@link StarkProgressIndicatorService} and creates an instance of the {@link StarkProgressIndicatorComponent} which is the one that actually displays the progress indicator (e.g., spinner).
*
* From then on you can control the state/visibility of the progress indicator programmatically by using the {@link StarkProgressIndicatorService}:
*
* ```typescript
* // the service should be injected in your component
* @Inject(STARK_PROGRESS_INDICATOR_SERVICE) private progressService: StarkProgressIndicatorService
*
* // then you can call the method to show the progress indicator
* this.progressService.show(this.progressIndicatorConfig.topic);
* // or to hide it
* this.progressService.hide(this.progressIndicatorConfig.topic);
* ```
*/
@Directive({
selector: directiveName
})
export class StarkProgressIndicatorDirective implements OnInit, OnDestroy {
@Input()
public starkProgressIndicator: StarkProgressIndicatorConfig;

public topic: string;
public type: StarkProgressIndicatorType | string;
public progressSubscription: Subscription;
private _componentFactory: ComponentFactory<StarkProgressIndicatorComponent>;
public _componentRef: ComponentRef<StarkProgressIndicatorComponent>;

public constructor(
@Inject(STARK_PROGRESS_INDICATOR_SERVICE) public _progressService: StarkProgressIndicatorService,
componentFactoryResolver: ComponentFactoryResolver,
private _viewContainer: ViewContainerRef,
protected renderer: Renderer2,
protected elementRef: ElementRef
) {
this._componentFactory = componentFactoryResolver.resolveComponentFactory(StarkProgressIndicatorComponent);
}

/**
* Registers an instance of progress indicator
*/
public registerInstance(progressConfig: StarkProgressIndicatorConfig): void {
this.topic = progressConfig.topic;

if (typeof StarkProgressIndicatorType[progressConfig.type] !== "undefined") {
this.type = StarkProgressIndicatorType[progressConfig.type];
} else {
this.type = StarkProgressIndicatorType.SPINNER;
}

this._progressService.register(this.topic, <StarkProgressIndicatorType>this.type);
}

/**
* The directive registers itself with the StarkProgressIndicator service
* The component to add is then created and inserted inside of the container.
* Finally, if the component should be hidden or shown, the stark-hide class is removed or add from it according to the wanted result
*/
public ngOnInit(): void {
this.registerInstance(this.starkProgressIndicator);

this._componentRef = this._viewContainer.createComponent(this._componentFactory);

// TODO The element is here added as a child, not as a sibling
// this.renderer.appendChild(this.elementRef.nativeElement, this._componentRef.location.nativeElement);

this._viewContainer.insert(this._componentRef.hostView);

// tslint:disable-next-line:bool-param-default
this.progressSubscription = this._progressService.isVisible(this.topic).subscribe((isVisible: boolean | undefined) => {
this._componentRef.instance.isShown = !!isVisible;
if (isVisible) {
this.renderer.addClass(this.elementRef.nativeElement, "stark-hide");
} else {
this.renderer.removeClass(this.elementRef.nativeElement, "stark-hide");
}
});
}

public ngOnDestroy(): void {
this._viewContainer.clear();
this.progressSubscription.unsubscribe();
this._progressService.deregister(this.topic);
}
}
3 changes: 3 additions & 0 deletions packages/stark-ui/src/modules/progress-indicator/entities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./entities/progress-indicator-config.entity.intf";
export * from "./entities/progress-indicator-config.entity";
export * from "./entities/progress-indicator-type.entity";
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { StarkProgressIndicatorType } from "./progress-indicator-type.entity";

/**
* Interface of progress indicator config
*/
export interface StarkProgressIndicatorConfig {
/**
* The topic that the progress indicator will subscribe to.
*/
topic: string;

/**
* Type of progress indicator: SPINNER, ...
*/
type: StarkProgressIndicatorType;

/**
* Whether the progress indicator is visible or not.
*/
visible?: boolean;

/**
* Number of listeners (progress indicator directives) subscribed to the same topic of this config.
*/
listenersCount?: number;

/**
* Number of listeners (progress indicator directives) that are still pending (those that have called
* the StarkProgressIndicatorService's show() method but have not called the hide() method yet)
*/
pendingListenersCount?: number;
}
Loading

0 comments on commit 5240bee

Please sign in to comment.