mirror of https://github.com/ghostfolio/ghostfolio
committed by
Thomas
10 changed files with 258 additions and 2 deletions
@ -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'); |
||||
|
}); |
||||
|
}); |
@ -1,7 +1,15 @@ |
|||||
import { NgModule } from '@angular/core'; |
import { NgModule } from '@angular/core'; |
||||
import { CommonModule } from '@angular/common'; |
import { CommonModule } from '@angular/common'; |
||||
|
import { ValueComponent } from './value/value.component'; |
||||
|
// import { GfValueModule } from './value/value.module';
|
||||
|
|
||||
@NgModule({ |
@NgModule({ |
||||
imports: [CommonModule] |
imports: [CommonModule/*, GfValueModule*/], |
||||
|
declarations: [ |
||||
|
ValueComponent |
||||
|
], |
||||
|
exports: [ |
||||
|
ValueComponent |
||||
|
] |
||||
}) |
}) |
||||
export class UiModule {} |
export class UiModule {} |
||||
|
@ -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> |
||||
|
|
@ -0,0 +1,5 @@ |
|||||
|
:host { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
font-variant-numeric: tabular-nums; |
||||
|
} |
@ -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(); |
||||
|
}); |
||||
|
}); |
@ -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 |
||||
|
} |
@ -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 {} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 {} |
Loading…
Reference in new issue