Feature/setup storybook (#332)
* Setup ui library with storybook * Add value component with story * Update changelog
This commit is contained in:
@@ -5,10 +5,10 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfSymbolIconModule } from '../symbol-icon/symbol-icon.module';
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { AccountsTableComponent } from './accounts-table.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -3,12 +3,12 @@ import { NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfDialogFooterModule } from '../dialog-footer/dialog-footer.module';
|
||||
import { GfDialogHeaderModule } from '../dialog-header/dialog-header.module';
|
||||
import { GfFearAndGreedIndexModule } from '../fear-and-greed-index/fear-and-greed-index.module';
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { PerformanceChartDialog } from './performance-chart-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { PortfolioPerformanceComponent } from './portfolio-performance.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { PortfolioSummaryComponent } from './portfolio-summary.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -3,11 +3,11 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { GfLineChartModule } from '@ghostfolio/client/components/line-chart/line-chart.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfDialogFooterModule } from '../../dialog-footer/dialog-footer.module';
|
||||
import { GfDialogHeaderModule } from '../../dialog-header/dialog-header.module';
|
||||
import { GfValueModule } from '../../value/value.module';
|
||||
import { PositionDetailDialog } from './position-detail-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -3,10 +3,10 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfTrendIndicatorModule } from '../trend-indicator/trend-indicator.module';
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { GfPositionDetailDialogModule } from './position-detail-dialog/position-detail-dialog.module';
|
||||
import { PositionComponent } from './position.component';
|
||||
|
||||
|
@@ -8,12 +8,12 @@ import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfNoTransactionsInfoModule } from '../no-transactions-info/no-transactions-info.module';
|
||||
import { GfPositionDetailDialogModule } from '../position/position-detail-dialog/position-detail-dialog.module';
|
||||
import { GfSymbolIconModule } from '../symbol-icon/symbol-icon.module';
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { PositionsTableComponent } from './positions-table.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -10,11 +10,11 @@ import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
|
||||
import { GfPositionDetailDialogModule } from '../position/position-detail-dialog/position-detail-dialog.module';
|
||||
import { GfSymbolIconModule } from '../symbol-icon/symbol-icon.module';
|
||||
import { GfValueModule } from '../value/value.module';
|
||||
import { TransactionsTableComponent } from './transactions-table.component';
|
||||
|
||||
@NgModule({
|
||||
|
@@ -1,45 +0,0 @@
|
||||
<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>
|
@@ -1,5 +0,0 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit
|
||||
} 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, OnInit {
|
||||
@Input() colorizeSign: boolean;
|
||||
@Input() currency: string;
|
||||
@Input() isCurrency: boolean;
|
||||
@Input() isInteger: boolean;
|
||||
@Input() isPercent: boolean;
|
||||
@Input() label: string;
|
||||
@Input() locale: string;
|
||||
@Input() position: string;
|
||||
@Input() size: string;
|
||||
@Input() value: number | string;
|
||||
|
||||
public absoluteValue: number;
|
||||
public formattedDate: string;
|
||||
public formattedValue: string;
|
||||
public isDate: boolean;
|
||||
public isNumber: boolean;
|
||||
public useAbsoluteValue = false;
|
||||
|
||||
public constructor() {}
|
||||
|
||||
public ngOnInit() {}
|
||||
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
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 {}
|
@@ -9,8 +9,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { GfValueModule } from '@ghostfolio/client/components/value/value.module';
|
||||
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
|
||||
import { GfValueModule } from '@ghostfolio/ui/value';
|
||||
|
||||
import { CreateOrUpdateTransactionDialog } from './create-or-update-transaction-dialog.component';
|
||||
|
||||
|
17
apps/ui-e2e/.eslintrc.json
Normal file
17
apps/ui-e2e/.eslintrc.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["src/plugins/index.js"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-undef": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
13
apps/ui-e2e/cypress.json
Normal file
13
apps/ui-e2e/cypress.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"fileServerFolder": ".",
|
||||
"fixturesFolder": "./src/fixtures",
|
||||
"integrationFolder": "./src/integration",
|
||||
"modifyObstructiveCode": false,
|
||||
"supportFile": "./src/support/index.ts",
|
||||
"pluginsFile": "./src/plugins/index",
|
||||
"video": true,
|
||||
"videosFolder": "../../dist/cypress/apps/ui-e2e/videos",
|
||||
"screenshotsFolder": "../../dist/cypress/apps/ui-e2e/screenshots",
|
||||
"chromeWebSecurity": false,
|
||||
"baseUrl": "http://localhost:4400"
|
||||
}
|
4
apps/ui-e2e/src/fixtures/example.json
Normal file
4
apps/ui-e2e/src/fixtures/example.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io"
|
||||
}
|
@@ -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');
|
||||
});
|
||||
});
|
22
apps/ui-e2e/src/plugins/index.js
Normal file
22
apps/ui-e2e/src/plugins/index.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor');
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
|
||||
// Preprocess Typescript file using Nx helper
|
||||
on('file:preprocessor', preprocessTypescript(config));
|
||||
};
|
33
apps/ui-e2e/src/support/commands.ts
Normal file
33
apps/ui-e2e/src/support/commands.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
17
apps/ui-e2e/src/support/index.ts
Normal file
17
apps/ui-e2e/src/support/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
10
apps/ui-e2e/tsconfig.json
Normal file
10
apps/ui-e2e/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "node"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js"]
|
||||
}
|
Reference in New Issue
Block a user