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 = { ...@@ -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 = { ...@@ -60,3 +60,11 @@ export const withoutBothPrices = {
...base, ...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null, fiat_price: null }) : null), 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 { export interface ChartTransactionItem {
date: string; date: string;
tx_count: number; tx_count: number | null;
} }
export interface ChartMarketItem { export interface ChartMarketItem {
date: string; date: string;
closing_price: string; closing_price: string | null;
market_cap?: string; market_cap?: string | null;
tvl?: string | null; tvl?: string | null;
} }
......
...@@ -22,6 +22,10 @@ const ChainIndicatorChartContainer = ({ data, isError, isPending }: Props) => { ...@@ -22,6 +22,10 @@ const ChainIndicatorChartContainer = ({ data, isError, isPending }: Props) => {
return <DataFetchAlert/>; return <DataFetchAlert/>;
} }
if (data[0].items.length === 0) {
return <span>no data</span>;
}
return <ChainIndicatorChart data={ data }/>; return <ChainIndicatorChart data={ data }/>;
})(); })();
......
...@@ -53,3 +53,44 @@ test.describe('daily txs chart', () => { ...@@ -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 React from 'react';
import type { TChainIndicator } from '../types'; import type { TChainIndicator } from '../types';
import type { TimeChartItem, TimeChartItemRaw } from 'ui/shared/chart/types';
import config from 'configs/app'; import config from 'configs/app';
import { sortByDateDesc } from 'ui/shared/chart/utils/sorts'; import { sortByDateDesc } from 'ui/shared/chart/utils/sorts';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity'; import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
import IconSvg from 'ui/shared/IconSvg'; 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'> = { const dailyTxsIndicator: TChainIndicator<'stats_charts_txs'> = {
id: 'daily_txs', id: 'daily_txs',
title: 'Daily transactions', 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"/>, 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.`, hint: `Number of transactions yesterday (0:00 - 23:59 UTC). The chart displays daily transactions for the past 30 days.`,
api: { api: {
...@@ -18,7 +31,9 @@ const dailyTxsIndicator: TChainIndicator<'stats_charts_txs'> = { ...@@ -18,7 +31,9 @@ const dailyTxsIndicator: TChainIndicator<'stats_charts_txs'> = {
dataFn: (response) => ([ { dataFn: (response) => ([ {
items: response.chart_data items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: item.tx_count })) .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', name: 'Tx/day',
valueFormatter: (x: number) => x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }), valueFormatter: (x: number) => x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
} ]), } ]),
...@@ -36,15 +51,19 @@ const nativeTokenData = { ...@@ -36,15 +51,19 @@ const nativeTokenData = {
const coinPriceIndicator: TChainIndicator<'stats_charts_market'> = { const coinPriceIndicator: TChainIndicator<'stats_charts_market'> = {
id: 'coin_price', id: 'coin_price',
title: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } 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 }/>, icon: <TokenEntity.Icon token={ nativeTokenData } boxSize={ 6 } marginRight={ 0 }/>,
hint: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } token daily price in USD.`, hint: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } token daily price in USD.`,
api: { api: {
resourceName: 'stats_charts_market', resourceName: 'stats_charts_market',
dataFn: (response) => ([ { dataFn: (response) => ([ {
items: response.chart_data items: response.chart_data
.map((item) => ({ date: new Date(item.date), value: Number(item.closing_price) })) .map((item) => ({ date: new Date(item.date), value: item.closing_price }))
.sort(sortByDateDesc), .sort(sortByDateDesc)
.reduceRight(nonNullTailReducer, [] as Array<TimeChartItemRaw>)
.map(mapNullToZero),
name: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } price`, name: `${ config.chain.governanceToken.symbol || config.chain.currency.symbol } price`,
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }), valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 6 }),
} ]), } ]),
...@@ -54,7 +73,9 @@ const coinPriceIndicator: TChainIndicator<'stats_charts_market'> = { ...@@ -54,7 +73,9 @@ const coinPriceIndicator: TChainIndicator<'stats_charts_market'> = {
const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = { const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = {
id: 'market_cap', id: 'market_cap',
title: '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"/>, icon: <IconSvg name="globe" boxSize={ 6 } bgColor="#6A5DCC" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len // 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.', 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'> = { ...@@ -65,11 +86,23 @@ const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = {
.map((item) => ( .map((item) => (
{ {
date: new Date(item.date), 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', 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'> = { ...@@ -77,7 +110,9 @@ const marketPriceIndicator: TChainIndicator<'stats_charts_market'> = {
const tvlIndicator: TChainIndicator<'stats_charts_market'> = { const tvlIndicator: TChainIndicator<'stats_charts_market'> = {
id: 'tvl', id: 'tvl',
title: 'Total value locked', 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"/>, icon: <IconSvg name="lock" boxSize={ 6 } bgColor="#517FDB" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
hint: 'Total value of digital assets locked or staked in a chain', hint: 'Total value of digital assets locked or staked in a chain',
...@@ -88,9 +123,11 @@ const tvlIndicator: TChainIndicator<'stats_charts_market'> = { ...@@ -88,9 +123,11 @@ const tvlIndicator: TChainIndicator<'stats_charts_market'> = {
.map((item) => ( .map((item) => (
{ {
date: new Date(item.date), 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', name: 'TVL',
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }), valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
} ]), } ]),
......
export interface TimeChartItemRaw {
date: Date;
dateLabel?: string;
value: number | string | null;
}
export interface TimeChartItem { export interface TimeChartItem {
date: Date; date: Date;
dateLabel?: string; dateLabel?: string;
......
import type { TimeChartItem } from '../types'; 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(); 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