Browse Source

Add value component with story

pull/332/head
Thomas Kaul 4 years ago
committed by Thomas
parent
commit
11436e41a4
  1. 7
      angular.json
  2. 6
      apps/ui-e2e/src/integration/value/value.component.spec.ts
  3. 10
      libs/ui/src/lib/ui.module.ts
  4. 46
      libs/ui/src/lib/value/value.component.html
  5. 5
      libs/ui/src/lib/value/value.component.scss
  6. 25
      libs/ui/src/lib/value/value.component.spec.ts
  7. 38
      libs/ui/src/lib/value/value.component.stories.ts
  8. 108
      libs/ui/src/lib/value/value.component.ts
  9. 13
      libs/ui/src/lib/value/value.module.ts
  10. 2
      package.json

7
angular.json

@ -245,9 +245,14 @@
},
"ui": {
"projectType": "library",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "libs/ui",
"sourceRoot": "libs/ui/src",
"prefix": "ghostfolio",
"prefix": "gf",
"architect": {
"test": {
"builder": "@nrwl/jest:jest",

6
apps/ui-e2e/src/integration/value/value.component.spec.ts

@ -0,0 +1,6 @@
describe('ui', () => {
beforeEach(() => cy.visit('/iframe.html?id=valuecomponent--loading'));
it('should render the component', () => {
cy.get('gf-value').should('exist');
});
});

10
libs/ui/src/lib/ui.module.ts

@ -1,7 +1,15 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ValueComponent } from './value/value.component';
// import { GfValueModule } from './value/value.module';
@NgModule({
imports: [CommonModule]
imports: [CommonModule/*, GfValueModule*/],
declarations: [
ValueComponent
],
exports: [
ValueComponent
]
})
export class UiModule {}

46
libs/ui/src/lib/value/value.component.html

@ -0,0 +1,46 @@
<ng-container *ngIf="value || value === 0 || value === null">
<div
class="d-flex"
[ngClass]="position === 'end' ? 'justify-content-end' : ''"
>
<ng-container *ngIf="isNumber || value === null">
<div *ngIf="colorizeSign && value > 0" class="mr-1 text-success">+</div>
<div *ngIf="colorizeSign && value < 0" class="mr-1 text-danger">-</div>
<div *ngIf="isPercent" [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
{{ formattedValue }}%
</div>
<div *ngIf="!isPercent" [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
<ng-container *ngIf="value === null">
<span class="text-monospace text-muted">***</span>
</ng-container>
<ng-container *ngIf="value !== null">
{{ formattedValue }}
</ng-container>
</div>
<small *ngIf="currency && size === 'medium'" class="ml-1">
{{ currency }}
</small>
<div *ngIf="currency && size !== 'medium'" class="ml-1">
{{ currency }}
</div>
</ng-container>
<ng-container *ngIf="isDate">
<div [ngClass]="size === 'medium' ? 'h4 mb-0' : ''">
{{ formattedDate }}
</div>
</ng-container>
</div>
<small *ngIf="label">
{{ label }}
</small>
</ng-container>
<ngx-skeleton-loader
*ngIf="value === undefined"
animation="pulse"
[theme]="{
height: '1.5rem',
width: '5rem'
}"
></ngx-skeleton-loader>

5
libs/ui/src/lib/value/value.component.scss

@ -0,0 +1,5 @@
:host {
display: flex;
flex-direction: column;
font-variant-numeric: tabular-nums;
}

25
libs/ui/src/lib/value/value.component.spec.ts

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ValueComponent } from './value.component';
describe('ValueComponent', () => {
let component: ValueComponent;
let fixture: ComponentFixture<ValueComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ValueComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ValueComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

38
libs/ui/src/lib/value/value.component.stories.ts

@ -0,0 +1,38 @@
import { moduleMetadata, Story, Meta } from '@storybook/angular';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { ValueComponent } from './value.component';
export default {
title: 'Value',
component: ValueComponent,
decorators: [
moduleMetadata({
imports: [NgxSkeletonLoaderModule],
})
],
} as Meta<ValueComponent>;
const Template: Story<ValueComponent> = (args: ValueComponent) => ({
props: args,
});
export const Loading = Template.bind({});
Loading.args = {
value: undefined
}
export const Integer = Template.bind({});
Integer.args = {
isInteger: true,
locale: 'en',
value: 7
}
export const Currency = Template.bind({});
Currency.args = {
currency: 'USD',
isInteger: true,
label: 'Label',
locale: 'en',
value: 7
}

108
libs/ui/src/lib/value/value.component.ts

@ -0,0 +1,108 @@
import {
ChangeDetectionStrategy,
Component,
Input,
OnChanges,
} from '@angular/core';
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
import { format, isDate } from 'date-fns';
import { isNumber } from 'lodash';
@Component({
selector: 'gf-value',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './value.component.html',
styleUrls: ['./value.component.scss']
})
export class ValueComponent implements OnChanges {
@Input() colorizeSign = false;
@Input() currency = '';
@Input() isCurrency = false;
@Input() isInteger = false;
@Input() isPercent = false;
@Input() label = '';
@Input() locale = '';
@Input() position = '';
@Input() size = '';
@Input() value: number | string = '';
public absoluteValue = 0;
public formattedDate = '';
public formattedValue = '';
public isDate = false;
public isNumber = false;
public useAbsoluteValue = false;
public constructor() {}
public ngOnChanges() {
if (this.value || this.value === 0) {
if (isNumber(this.value)) {
this.isDate = false;
this.isNumber = true;
this.absoluteValue = Math.abs(<number>this.value);
if (this.colorizeSign) {
this.useAbsoluteValue = true;
if (this.currency || this.isCurrency) {
try {
this.formattedValue = this.absoluteValue.toLocaleString(
this.locale,
{
maximumFractionDigits: 2,
minimumFractionDigits: 2
}
);
} catch {}
} else if (this.isPercent) {
try {
this.formattedValue = (this.absoluteValue * 100).toLocaleString(
this.locale,
{
maximumFractionDigits: 2,
minimumFractionDigits: 2
}
);
} catch {}
}
} else if (this.isPercent) {
try {
this.formattedValue = (this.value * 100).toLocaleString(
this.locale,
{
maximumFractionDigits: 2,
minimumFractionDigits: 2
}
);
} catch {}
} else if (this.currency || this.isCurrency) {
try {
this.formattedValue = this.value?.toLocaleString(this.locale, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
});
} catch {}
} else if (this.isInteger) {
try {
this.formattedValue = this.value?.toLocaleString(this.locale, {
maximumFractionDigits: 0,
minimumFractionDigits: 0
});
} catch {}
}
} else {
try {
if (isDate(new Date(this.value))) {
this.isDate = true;
this.isNumber = false;
this.formattedDate = format(
new Date(<string>this.value),
DEFAULT_DATE_FORMAT
);
}
} catch {}
}
}
}
}

13
libs/ui/src/lib/value/value.module.ts

@ -0,0 +1,13 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { ValueComponent } from './value.component';
@NgModule({
declarations: [ValueComponent],
exports: [ValueComponent],
imports: [CommonModule, NgxSkeletonLoaderModule],
providers: []
})
export class GfValueModule {}

2
package.json

@ -14,6 +14,7 @@
"affected:test": "nx affected:test",
"angular": "node --max_old_space_size=32768 ./node_modules/@angular/cli/bin/ng",
"build:all": "ng build --configuration production api && ng build --configuration production client && yarn replace-placeholders-in-build",
"build:storybook": "nx run ui:build-storybook",
"clean": "rimraf dist",
"database:format-schema": "prisma format",
"database:generate-typings": "prisma generate",
@ -37,6 +38,7 @@
"start:client": "ng serve client --hmr -o",
"start:prod": "node apps/api/main",
"start:server": "nx serve api --watch",
"start:storybook": "nx run ui:storybook",
"test": "nx test",
"ts-node": "ts-node --compiler-options '{\"module\":\"CommonJS\"}'",
"update": "nx migrate latest",

Loading…
Cancel
Save