Commit e1317844 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Gas tracker improvements (#1941)

* Alter gas tracker behaviour in case of null gas prices from backend

* display small amounts of gwei

* remove links to stats if feature is not enabled

* fix test
parent c75db4bd
...@@ -65,3 +65,22 @@ test('with zero values', async({ render, page }) => { ...@@ -65,3 +65,22 @@ test('with zero values', async({ render, page }) => {
); );
await expect(page).toHaveScreenshot({ clip }); await expect(page).toHaveScreenshot({ clip });
}); });
test('with small values', async({ render, page }) => {
const data = {
fiat_price: '0.0042',
price: 0.042,
time: 0,
base_fee: 0,
priority_fee: 0,
};
await render(
<GasTrackerPriceSnippet
data={ data }
type="slow"
isLoading={ false }
/>,
);
await expect(page).toHaveScreenshot({ clip });
});
import { test, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test'; import type { Locator } from '@playwright/test';
import React from 'react'; import React from 'react';
import * as statsMock from 'mocks/stats/index'; import * as statsMock from 'mocks/stats/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import { test, expect } from 'playwright/lib';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
import Stats from './Stats'; import Stats from './Stats';
const API_URL = buildApiUrl('stats');
test.describe('all items', () => { test.describe('all items', () => {
let component: Locator; let component: Locator;
test.beforeEach(async({ page, mount }) => { test.beforeEach(async({ render, mockApiResponse }) => {
await page.route(API_URL, (route) => route.fulfill({ await mockApiResponse('stats', statsMock.withBtcLocked);
status: 200, component = await render(<Stats/>);
body: JSON.stringify(statsMock.withBtcLocked),
}));
component = await mount(
<TestApp>
<Stats/>
</TestApp>,
);
}); });
test('+@mobile +@dark-mode', async() => { test('+@mobile +@dark-mode', async() => {
...@@ -41,51 +28,21 @@ test.describe('all items', () => { ...@@ -41,51 +28,21 @@ test.describe('all items', () => {
}); });
}); });
test.describe('4 items', () => { test('4 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => {
const extendedTest = test.extend({ await mockEnvs([
context: contextWithEnvs([ [ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ],
{ name: 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', value: 'false' }, ]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any await mockApiResponse('stats', statsMock.base);
]) as any, const component = await render(<Stats/>);
});
extendedTest('default view +@mobile -@default', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
const component = await mount(
<TestApp>
<Stats/>
</TestApp>,
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
});
}); });
test.describe('3 items', () => { test('3 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => {
const extendedTest = test.extend({ await mockEnvs([
context: contextWithEnvs([ [ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ],
{ name: 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', value: 'false' }, [ 'NEXT_PUBLIC_GAS_TRACKER_ENABLED', 'false' ],
{ name: 'NEXT_PUBLIC_GAS_TRACKER_ENABLED', value: 'false' }, ]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any await mockApiResponse('stats', statsMock.base);
]) as any, const component = await render(<Stats/>);
});
extendedTest('default view +@mobile -@default', async({ mount, page }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
}));
const component = await mount(
<TestApp>
<Stats/>
</TestApp>,
);
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
});
}); });
...@@ -14,11 +14,11 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -14,11 +14,11 @@ import IconSvg from 'ui/shared/IconSvg';
import StatsItem from './StatsItem'; import StatsItem from './StatsItem';
const hasGasTracker = config.features.gasTracker.isEnabled;
const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime; const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime;
const rollupFeature = config.features.rollup; const rollupFeature = config.features.rollup;
const Stats = () => { const Stats = () => {
const [ hasGasTracker, setHasGasTracker ] = React.useState(config.features.gasTracker.isEnabled);
const { data, isPlaceholderData, isError, dataUpdatedAt } = useApiQuery('stats', { const { data, isPlaceholderData, isError, dataUpdatedAt } = useApiQuery('stats', {
queryOptions: { queryOptions: {
refetchOnMount: false, refetchOnMount: false,
...@@ -26,6 +26,14 @@ const Stats = () => { ...@@ -26,6 +26,14 @@ const Stats = () => {
}, },
}); });
React.useEffect(() => {
if (!isPlaceholderData && !data?.gas_prices?.average) {
setHasGasTracker(false);
}
// should run only after initial fetch
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ isPlaceholderData ]);
const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', { const zkEvmLatestBatchQuery = useApiQuery('homepage_zkevm_latest_batch', {
queryOptions: { queryOptions: {
placeholderData: 12345, placeholderData: 12345,
...@@ -61,7 +69,7 @@ const Stats = () => { ...@@ -61,7 +69,7 @@ const Stats = () => {
data.rootstock_locked_btc && itemsCount++; data.rootstock_locked_btc && itemsCount++;
rollupFeature.isEnabled && data.last_output_root_size && itemsCount++; rollupFeature.isEnabled && data.last_output_root_size && itemsCount++;
const isOdd = Boolean(itemsCount % 2); const isOdd = Boolean(itemsCount % 2);
const gasInfoTooltip = hasGasTracker && data.gas_prices ? ( const gasInfoTooltip = hasGasTracker && data.gas_prices && data.gas_prices.average ? (
<GasInfoTooltip data={ data } dataUpdatedAt={ dataUpdatedAt }> <GasInfoTooltip data={ data } dataUpdatedAt={ dataUpdatedAt }>
<IconSvg <IconSvg
isLoading={ isLoading } isLoading={ isLoading }
...@@ -141,7 +149,7 @@ const Stats = () => { ...@@ -141,7 +149,7 @@ const Stats = () => {
<StatsItem <StatsItem
icon="gas" icon="gas"
title="Gas tracker" title="Gas tracker"
value={ <GasPrice data={ data.gas_prices.average }/> } value={ data.gas_prices.average ? <GasPrice data={ data.gas_prices.average }/> : 'N/A' }
_last={ isOdd ? lastItemTouchStyle : undefined } _last={ isOdd ? lastItemTouchStyle : undefined }
tooltip={ gasInfoTooltip } tooltip={ gasInfoTooltip }
isLoading={ isLoading } isLoading={ isLoading }
......
import { Box, Flex, Skeleton, chakra } from '@chakra-ui/react'; import { Alert, Box, Flex, Skeleton, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
...@@ -62,6 +62,23 @@ const GasTracker = () => { ...@@ -62,6 +62,23 @@ const GasTracker = () => {
</Flex> </Flex>
); );
const content = (() => {
if (!isPlaceholderData && data?.gas_prices?.slow === null && data?.gas_prices.average === null && data.gas_prices.fast === null) {
return <Alert status="warning">No data available yet</Alert>;
}
return (
<>
{ data?.gas_prices && <GasTrackerPrices prices={ data.gas_prices } isLoading={ isLoading }/> }
{ config.features.stats.isEnabled && (
<Box mt={ 12 }>
<GasTrackerChart/>
</Box>
) }
</>
);
})();
return ( return (
<> <>
<PageTitle <PageTitle
...@@ -69,10 +86,7 @@ const GasTracker = () => { ...@@ -69,10 +86,7 @@ const GasTracker = () => {
secondRow={ titleSecondRow } secondRow={ titleSecondRow }
withTextAd withTextAd
/> />
{ data?.gas_prices && <GasTrackerPrices prices={ data.gas_prices } isLoading={ isLoading }/> } { content }
<Box mt={ 12 }>
<GasTrackerChart/>
</Box>
</> </>
); );
}; };
......
...@@ -9,6 +9,11 @@ export default function formatGasValue(data: GasPriceInfo, unit: GasUnit) { ...@@ -9,6 +9,11 @@ export default function formatGasValue(data: GasPriceInfo, unit: GasUnit) {
if (!data.price) { if (!data.price) {
return `N/A ${ currencyUnits.gwei }`; return `N/A ${ currencyUnits.gwei }`;
} }
if (Number(data.price) < 0.1) {
return `< 0.1 ${ currencyUnits.gwei }`;
}
return `${ Number(data.price).toLocaleString(undefined, { maximumFractionDigits: 1 }) } ${ currencyUnits.gwei }`; return `${ Number(data.price).toLocaleString(undefined, { maximumFractionDigits: 1 }) } ${ currencyUnits.gwei }`;
} }
......
...@@ -46,7 +46,7 @@ const TxsStats = () => { ...@@ -46,7 +46,7 @@ const TxsStats = () => {
value={ Number(txsStatsQuery.data?.transactions_count_24h).toLocaleString() } value={ Number(txsStatsQuery.data?.transactions_count_24h).toLocaleString() }
period="24h" period="24h"
isLoading={ txsStatsQuery.isPlaceholderData } isLoading={ txsStatsQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'newTxns' } }} href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'newTxns' } } : undefined }
/> />
<StatsWidget <StatsWidget
label="Pending transactions" label="Pending transactions"
...@@ -63,7 +63,7 @@ const TxsStats = () => { ...@@ -63,7 +63,7 @@ const TxsStats = () => {
valuePostfix={ thinsp + config.chain.currency.symbol } valuePostfix={ thinsp + config.chain.currency.symbol }
period="24h" period="24h"
isLoading={ txsStatsQuery.isPlaceholderData } isLoading={ txsStatsQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'txnsFee' } }} href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'txnsFee' } } : undefined }
/> />
<StatsWidget <StatsWidget
label="Avg. transaction fee" label="Avg. transaction fee"
...@@ -72,7 +72,7 @@ const TxsStats = () => { ...@@ -72,7 +72,7 @@ const TxsStats = () => {
valuePostfix={ txFeeAvg.usd ? undefined : thinsp + config.chain.currency.symbol } valuePostfix={ txFeeAvg.usd ? undefined : thinsp + config.chain.currency.symbol }
period="24h" period="24h"
isLoading={ txsStatsQuery.isPlaceholderData } isLoading={ txsStatsQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'averageTxnFee' } }} href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'averageTxnFee' } } : undefined }
/> />
</Box> </Box>
); );
......
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
import { VERIFIED_CONTRACTS_COUNTERS } from 'stubs/contract'; import { VERIFIED_CONTRACTS_COUNTERS } from 'stubs/contract';
import StatsWidget from 'ui/shared/stats/StatsWidget'; import StatsWidget from 'ui/shared/stats/StatsWidget';
...@@ -24,7 +25,7 @@ const VerifiedContractsCounters = () => { ...@@ -24,7 +25,7 @@ const VerifiedContractsCounters = () => {
diff={ countersQuery.data.new_smart_contracts_24h } diff={ countersQuery.data.new_smart_contracts_24h }
diffFormatted={ Number(countersQuery.data.new_smart_contracts_24h).toLocaleString() } diffFormatted={ Number(countersQuery.data.new_smart_contracts_24h).toLocaleString() }
isLoading={ countersQuery.isPlaceholderData } isLoading={ countersQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'contractsGrowth' } }} href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'contractsGrowth' } } : undefined }
/> />
<StatsWidget <StatsWidget
label="Verified contracts" label="Verified contracts"
...@@ -32,7 +33,7 @@ const VerifiedContractsCounters = () => { ...@@ -32,7 +33,7 @@ const VerifiedContractsCounters = () => {
diff={ countersQuery.data.new_verified_smart_contracts_24h } diff={ countersQuery.data.new_verified_smart_contracts_24h }
diffFormatted={ Number(countersQuery.data.new_verified_smart_contracts_24h).toLocaleString() } diffFormatted={ Number(countersQuery.data.new_verified_smart_contracts_24h).toLocaleString() }
isLoading={ countersQuery.isPlaceholderData } isLoading={ countersQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'verifiedContractsGrowth' } }} href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'verifiedContractsGrowth' } } : undefined }
/> />
</Box> </Box>
); );
......
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