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 }) => {
);
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 React from 'react';
import * as statsMock from 'mocks/stats/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import { test, expect } from 'playwright/lib';
import * as configs from 'playwright/utils/configs';
import Stats from './Stats';
const API_URL = buildApiUrl('stats');
test.describe('all items', () => {
let component: Locator;
test.beforeEach(async({ page, mount }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.withBtcLocked),
}));
component = await mount(
<TestApp>
<Stats/>
</TestApp>,
);
test.beforeEach(async({ render, mockApiResponse }) => {
await mockApiResponse('stats', statsMock.withBtcLocked);
component = await render(<Stats/>);
});
test('+@mobile +@dark-mode', async() => {
......@@ -41,51 +28,21 @@ test.describe('all items', () => {
});
});
test.describe('4 items', () => {
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', value: 'false' },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
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();
});
test('4 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ],
]);
await mockApiResponse('stats', statsMock.base);
const component = await render(<Stats/>);
await expect(component).toHaveScreenshot();
});
test.describe('3 items', () => {
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', value: 'false' },
{ name: 'NEXT_PUBLIC_GAS_TRACKER_ENABLED', value: 'false' },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
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();
});
test('3 items default view +@mobile -@default', async({ render, mockApiResponse, mockEnvs }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME', 'false' ],
[ 'NEXT_PUBLIC_GAS_TRACKER_ENABLED', 'false' ],
]);
await mockApiResponse('stats', statsMock.base);
const component = await render(<Stats/>);
await expect(component).toHaveScreenshot();
});
......@@ -14,11 +14,11 @@ import IconSvg from 'ui/shared/IconSvg';
import StatsItem from './StatsItem';
const hasGasTracker = config.features.gasTracker.isEnabled;
const hasAvgBlockTime = config.UI.homepage.showAvgBlockTime;
const rollupFeature = config.features.rollup;
const Stats = () => {
const [ hasGasTracker, setHasGasTracker ] = React.useState(config.features.gasTracker.isEnabled);
const { data, isPlaceholderData, isError, dataUpdatedAt } = useApiQuery('stats', {
queryOptions: {
refetchOnMount: false,
......@@ -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', {
queryOptions: {
placeholderData: 12345,
......@@ -61,7 +69,7 @@ const Stats = () => {
data.rootstock_locked_btc && itemsCount++;
rollupFeature.isEnabled && data.last_output_root_size && itemsCount++;
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 }>
<IconSvg
isLoading={ isLoading }
......@@ -141,7 +149,7 @@ const Stats = () => {
<StatsItem
icon="gas"
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 }
tooltip={ gasInfoTooltip }
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 config from 'configs/app';
......@@ -62,6 +62,23 @@ const GasTracker = () => {
</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 (
<>
<PageTitle
......@@ -69,10 +86,7 @@ const GasTracker = () => {
secondRow={ titleSecondRow }
withTextAd
/>
{ data?.gas_prices && <GasTrackerPrices prices={ data.gas_prices } isLoading={ isLoading }/> }
<Box mt={ 12 }>
<GasTrackerChart/>
</Box>
{ content }
</>
);
};
......
......@@ -9,6 +9,11 @@ export default function formatGasValue(data: GasPriceInfo, unit: GasUnit) {
if (!data.price) {
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 }`;
}
......
......@@ -46,7 +46,7 @@ const TxsStats = () => {
value={ Number(txsStatsQuery.data?.transactions_count_24h).toLocaleString() }
period="24h"
isLoading={ txsStatsQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'newTxns' } }}
href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'newTxns' } } : undefined }
/>
<StatsWidget
label="Pending transactions"
......@@ -63,7 +63,7 @@ const TxsStats = () => {
valuePostfix={ thinsp + config.chain.currency.symbol }
period="24h"
isLoading={ txsStatsQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'txnsFee' } }}
href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'txnsFee' } } : undefined }
/>
<StatsWidget
label="Avg. transaction fee"
......@@ -72,7 +72,7 @@ const TxsStats = () => {
valuePostfix={ txFeeAvg.usd ? undefined : thinsp + config.chain.currency.symbol }
period="24h"
isLoading={ txsStatsQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'averageTxnFee' } }}
href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'averageTxnFee' } } : undefined }
/>
</Box>
);
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { VERIFIED_CONTRACTS_COUNTERS } from 'stubs/contract';
import StatsWidget from 'ui/shared/stats/StatsWidget';
......@@ -24,7 +25,7 @@ const VerifiedContractsCounters = () => {
diff={ countersQuery.data.new_smart_contracts_24h }
diffFormatted={ Number(countersQuery.data.new_smart_contracts_24h).toLocaleString() }
isLoading={ countersQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'contractsGrowth' } }}
href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'contractsGrowth' } } : undefined }
/>
<StatsWidget
label="Verified contracts"
......@@ -32,7 +33,7 @@ const VerifiedContractsCounters = () => {
diff={ countersQuery.data.new_verified_smart_contracts_24h }
diffFormatted={ Number(countersQuery.data.new_verified_smart_contracts_24h).toLocaleString() }
isLoading={ countersQuery.isPlaceholderData }
href={{ pathname: '/stats', query: { chartId: 'verifiedContractsGrowth' } }}
href={ config.features.stats.isEnabled ? { pathname: '/stats', query: { chartId: 'verifiedContractsGrowth' } } : undefined }
/>
</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