Commit b2a79e16 authored by tom's avatar tom

Add a decimal part to the Market cap graph value and handle null values

parent 9dbb2b0c
......@@ -126,3 +126,24 @@ export const base = {
},
],
};
export const partialData = {
chart_data: [
{ date: '2022-11-28', tx_count: 26815 },
{ date: '2022-11-27', tx_count: 34784 },
{ date: '2022-11-26', tx_count: 77527 },
{ date: '2022-11-25', tx_count: null },
{ date: '2022-11-24', tx_count: null },
{ date: '2022-11-23', tx_count: null },
{ date: '2022-11-22', tx_count: 63433 },
{ date: '2022-11-21', tx_count: null },
],
};
export const noData = {
chart_data: [
{ date: '2022-11-25', tx_count: null },
{ date: '2022-11-24', tx_count: null },
{ date: '2022-11-23', tx_count: null },
],
};
......@@ -60,3 +60,11 @@ export const withoutBothPrices = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null, fiat_price: null }) : null),
};
export const noChartData = {
...base,
transactions_today: null,
coin_price: null,
market_cap: null,
tvl: null,
};
export interface ChartTransactionItem {
date: string;
tx_count: number;
tx_count: number | null;
}
export interface ChartMarketItem {
date: string;
closing_price: string;
market_cap?: string;
closing_price: string | null;
market_cap?: string | null;
tvl?: string | null;
}
......
......@@ -22,6 +22,10 @@ const ChainIndicatorChartContainer = ({ data, isError, isPending }: Props) => {
return <DataFetchAlert/>;
}
if (data[0].items.length === 0) {
return <span>no data</span>;
}
return <ChainIndicatorChart data={ data }/>;
})();
......
......@@ -53,3 +53,44 @@ test.describe('daily txs chart', () => {
});
});
});
test('partial data', async({ page, mount }) => {
await page.route(STATS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
await page.route(TX_CHART_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(dailyTxsMock.partialData),
}));
const component = await mount(
<TestApp>
<ChainIndicators/>
</TestApp>,
);
await page.waitForFunction(() => {
return document.querySelector('path[data-name="gradient-chart-area"]')?.getAttribute('opacity') === '1';
});
await expect(component).toHaveScreenshot();
});
test('no data', async({ page, mount }) => {
await page.route(STATS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.noChartData),
}));
await page.route(TX_CHART_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(dailyTxsMock.noData),
}));
const component = await mount(
<TestApp>
<ChainIndicators/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
import React from 'react';
import type { TChainIndicator } from '../types';
import type { TimeChartItem, TimeChartItemRaw } from 'ui/shared/chart/types';
import config from 'configs/app';
import { sortByDateDesc } from 'ui/shared/chart/utils/sorts';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import IconSvg from 'ui/shared/IconSvg';
const nonNullTailReducer = (result: Array<TimeChartItemRaw>, item: TimeChartItemRaw) => {
if (item.value === null && result.length === 0) {
return result;
}
result.unshift(item);
return result;
};
const mapNullToZero: (item: TimeChartItemRaw) => TimeChartItem = (item) => ({ ...item, value: Number(item.value) });
const dailyTxsIndicator: TChainIndicator<'stats_charts_txs'> = {
id: 'daily_txs',
title: 'Daily transactions',
value: (stats) => Number(stats.transactions_today).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
value: (stats) => stats.transactions_today === null ?
'N/A' :
Number(stats.transactions_today).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
icon: <IconSvg name="transactions" boxSize={ 6 } bgColor="#56ACD1" borderRadius="base" color="white"/>,
hint: `Number of transactions yesterday (0:00 - 23:59 UTC). The chart displays daily transactions for the past 30 days.`,
api: {
......@@ -18,7 +31,9 @@ const dailyTxsIndicator: TChainIndicator<'stats_charts_txs'> = {
dataFn: (response) => ([ {
items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: item.tx_count }))
.sort(sortByDateDesc),
.sort(sortByDateDesc)
.reduceRight(nonNullTailReducer, [] as Array<TimeChartItemRaw>)
.map(mapNullToZero),
name: 'Tx/day',
valueFormatter: (x: number) => x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
} ]),
......@@ -36,15 +51,19 @@ const nativeTokenData = {
const coinPriceIndicator: TChainIndicator<'stats_charts_market'> = {
id: 'coin_price',
title: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } price`,
value: (stats) => '$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
value: (stats) => stats.coin_price === null ?
'$N/A' :
'$' + Number(stats.coin_price).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
icon: <TokenEntity.Icon token={ nativeTokenData } boxSize={ 6 } marginRight={ 0 }/>,
hint: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } token daily price in USD.`,
api: {
resourceName: 'stats_charts_market',
dataFn: (response) => ([ {
items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) }))
.sort(sortByDateDesc),
.map((item) => ({ date: new Date(item.date), value: item.closing_price }))
.sort(sortByDateDesc)
.reduceRight(nonNullTailReducer, [] as Array<TimeChartItemRaw>)
.map(mapNullToZero),
name: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } price`,
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
} ]),
......@@ -54,7 +73,9 @@ const coinPriceIndicator: TChainIndicator<'stats_charts_market'> = {
const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = {
id: 'market_cap',
title: 'Market cap',
value: (stats) => '$' + Number(stats.market_cap).toLocaleString(undefined, { maximumFractionDigits: 0, notation: 'compact' }),
value: (stats) => stats.market_cap === null ?
'$N/A' :
'$' + Number(stats.market_cap).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
icon: <IconSvg name="globe" boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len
hint: 'The total market value of a cryptocurrency\'s circulating supply. It is analogous to the free-float capitalization in the stock market. Market Cap = Current Price x Circulating Supply.',
......@@ -65,11 +86,23 @@ const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = {
.map((item) => (
{
date: new Date(item.date),
value: item.market_cap ? Number(item.market_cap) : Number(item.closing_price) * Number(response.available_supply),
value: (() => {
if (item.market_cap !== undefined) {
return item.market_cap;
}
if (item.closing_price === null) {
return null;
}
return Number(item.closing_price) * Number(response.available_supply);
})(),
}))
.sort(sortByDateDesc),
.sort(sortByDateDesc)
.reduceRight(nonNullTailReducer, [] as Array<TimeChartItemRaw>)
.map(mapNullToZero),
name: 'Market cap',
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 0 }),
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 2 }),
} ]),
},
};
......@@ -77,7 +110,9 @@ const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = {
const tvlIndicator: TChainIndicator<'stats_charts_market'> = {
id: 'tvl',
title: 'Total value locked',
value: (stats) => '$' + Number(stats.tvl).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
value: (stats) => stats.tvl === null ?
'$N/A' :
'$' + Number(stats.tvl).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
icon: <IconSvg name="lock" boxSize={ 6 } bgColor="#517FDB" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len
hint: 'Total value of digital assets locked or staked in a chain',
......@@ -88,9 +123,11 @@ const tvlIndicator: TChainIndicator<'stats_charts_market'> = {
.map((item) => (
{
date: new Date(item.date),
value: item.tvl ? Number(item.tvl) : 0,
value: item.tvl !== undefined ? item.tvl : 0,
}))
.sort(sortByDateDesc),
.sort(sortByDateDesc)
.reduceRight(nonNullTailReducer, [] as Array<TimeChartItemRaw>)
.map(mapNullToZero),
name: 'TVL',
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
} ]),
......
export interface TimeChartItemRaw {
date: Date;
dateLabel?: string;
value: number | string | null;
}
export interface TimeChartItem {
date: Date;
dateLabel?: string;
......
import type { TimeChartItem } from '../types';
export const sortByDateDesc = (a: TimeChartItem, b: TimeChartItem) => {
export const sortByDateDesc = (a: Pick<TimeChartItem, 'date'>, b: Pick<TimeChartItem, 'date'>) => {
return a.date.getTime() - b.date.getTime();
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment