Skip to content

Commit

Permalink
Fix RSK stats - transaction fees precision (#1602)
Browse files Browse the repository at this point in the history
* Fix RSK stats - transaction fees precision

Fixes #1473

* update screenshot
  • Loading branch information
tom2drum authored Feb 16, 2024
1 parent b2432b3 commit 8d6429d
Show file tree
Hide file tree
Showing 28 changed files with 115 additions and 56 deletions.
2 changes: 1 addition & 1 deletion configs/envs/.env.eth
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s.blockscout.com
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
Expand Down
2 changes: 1 addition & 1 deletion icons/swap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 9 additions & 9 deletions ui/home/indicators/ChainIndicatorChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,28 @@ const ChainIndicatorChart = ({ data }: Props) => {
const axesConfig = React.useMemo(() => {
return {
x: { ticks: 4 },
y: { ticks: 3, nice: true },
y: { ticks: 3, nice: true, noLabel: true },
};
}, [ ]);

const { rect, ref, axis, innerWidth, innerHeight } = useTimeChartController({
const { rect, ref, axes, innerWidth, innerHeight, chartMargin } = useTimeChartController({
data,
margin: CHART_MARGIN,
axesConfig,
});

return (
<svg width="100%" height="100%" ref={ ref } cursor="pointer">
<g transform={ `translate(${ CHART_MARGIN?.left || 0 },${ CHART_MARGIN?.top || 0 })` } opacity={ rect ? 1 : 0 }>
<g transform={ `translate(${ chartMargin.left || 0 },${ chartMargin.top || 0 })` } opacity={ rect ? 1 : 0 }>
<ChartArea
data={ data[0].items }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
/>
<ChartLine
data={ data[0].items }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
stroke={ lineColor }
animation="left"
strokeWidth={ 3 }
Expand All @@ -54,8 +54,8 @@ const ChainIndicatorChart = ({ data }: Props) => {
anchorEl={ overlayRef.current }
width={ innerWidth }
height={ innerHeight }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
data={ data }
/>
</ChartOverlay>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ui/shared/chart/ChartTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const ChartTooltip = ({ xScale, yScale, width, tooltipWidth = 200, height, data,
.selectAll<Element, TimeChartData>('.ChartTooltip__value')
.filter((td, tIndex) => tIndex === i)
.text(
(data[i].valueFormatter?.(d.value) || d.value.toLocaleString()) +
(data[i].valueFormatter?.(d.value) || d.value.toLocaleString(undefined, { minimumSignificantDigits: 1 })) +
(data[i].units ? ` ${ data[i].units }` : ''),
)
.nodes();
Expand Down
58 changes: 58 additions & 0 deletions ui/shared/chart/ChartWidget.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,61 @@ test('error', async({ mount }) => {

await expect(component).toHaveScreenshot();
});

test('small values', async({ mount, page }) => {
const modifiedProps = {
...props,
items: [
{ date: new Date('2023-02-13'), value: 0.000005041012112611958 },
{ date: new Date('2023-02-14'), value: 0.000004781545670577531 },
{ date: new Date('2023-02-15'), value: 0.00000520510604212437 },
{ date: new Date('2023-02-16'), value: 0.000005274901030625893 },
{ date: new Date('2023-02-17'), value: 0.00000534325322320271 },
{ date: new Date('2023-02-18'), value: 0.00000579140116207668 },
{ date: new Date('2023-02-19'), value: 0.000004878307079043056 },
{ date: new Date('2023-02-20'), value: 0.0000053454186920910215 },
{ date: new Date('2023-02-21'), value: 0.000005770588532081243 },
{ date: new Date('2023-02-22'), value: 0.00000589334810122426 },
{ date: new Date('2023-02-23'), value: 0.00000547040196358741 },
],
};

const component = await mount(
<TestApp>
<ChartWidget { ...modifiedProps }/>
</TestApp>,
);
await page.waitForFunction(() => {
return document.querySelector('path[data-name="chart-Nativecoincirculatingsupply-small"]')?.getAttribute('opacity') === '1';
});
await expect(component).toHaveScreenshot();
});

test('small variations in big values', async({ mount, page }) => {
const modifiedProps = {
...props,
items: [
{ date: new Date('2023-02-13'), value: 8886203 },
{ date: new Date('2023-02-14'), value: 8890184 },
{ date: new Date('2023-02-15'), value: 8893483 },
{ date: new Date('2023-02-16'), value: 8897924 },
{ date: new Date('2023-02-17'), value: 8902268 },
{ date: new Date('2023-02-18'), value: 8906320 },
{ date: new Date('2023-02-19'), value: 8910264 },
{ date: new Date('2023-02-20'), value: 8914827 },
{ date: new Date('2023-02-21'), value: 8918592 },
{ date: new Date('2023-02-22'), value: 8921988 },
{ date: new Date('2023-02-23'), value: 8922206 },
],
};

const component = await mount(
<TestApp>
<ChartWidget { ...modifiedProps }/>
</TestApp>,
);
await page.waitForFunction(() => {
return document.querySelector('path[data-name="chart-Nativecoincirculatingsupply-small"]')?.getAttribute('opacity') === '1';
});
await expect(component).toHaveScreenshot();
});
32 changes: 16 additions & 16 deletions ui/shared/chart/ChartWidgetGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useToken } from '@chakra-ui/react';
import * as d3 from 'd3';
import React from 'react';

import type { ChartMargin, TimeChartItem } from 'ui/shared/chart/types';
import type { ChartMargin, TimeChartData, TimeChartItem } from 'ui/shared/chart/types';

import dayjs from 'lib/date/dayjs';
import useIsMobile from 'lib/hooks/useIsMobile';
Expand All @@ -27,7 +27,7 @@ interface Props {

// temporarily turn off the data aggregation, we need a better algorithm for that
const MAX_SHOW_ITEMS = 100_000_000_000;
const DEFAULT_CHART_MARGIN = { bottom: 20, left: 40, right: 20, top: 10 };
const DEFAULT_CHART_MARGIN = { bottom: 20, left: 10, right: 20, top: 10 };

const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title, margin: marginProps, units }: Props) => {
const isMobile = useIsMobile();
Expand All @@ -51,7 +51,7 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
}
}, [ isGroupedValues, rangedItems ]);

const chartData = React.useMemo(() => ([ { items: displayedData, name: 'Value', color, units } ]), [ color, displayedData, units ]);
const chartData: TimeChartData = React.useMemo(() => ([ { items: displayedData, name: 'Value', color, units } ]), [ color, displayedData, units ]);

const margin: ChartMargin = React.useMemo(() => ({ ...DEFAULT_CHART_MARGIN, ...marginProps }), [ marginProps ]);
const axesConfig = React.useMemo(() => {
Expand All @@ -72,7 +72,7 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
innerWidth,
innerHeight,
chartMargin,
axis,
axes,
} = useTimeChartController({
data: chartData,
margin,
Expand All @@ -96,7 +96,7 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
<g transform={ `translate(${ chartMargin?.left || 0 },${ chartMargin?.top || 0 })` }>
<ChartGridLine
type="horizontal"
scale={ axis.y.scale }
scale={ axes.y.scale }
ticks={ axesConfig.y.ticks }
size={ innerWidth }
disableAnimation
Expand All @@ -106,34 +106,34 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
id={ chartId }
data={ displayedData }
color={ color }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
/>

<ChartLine
data={ displayedData }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
stroke={ color }
animation="none"
strokeWidth={ isMobile ? 1 : 2 }
/>

<ChartAxis
type="left"
scale={ axis.y.scale }
scale={ axes.y.scale }
ticks={ axesConfig.y.ticks }
tickFormatGenerator={ axis.y.tickFormatter }
tickFormatGenerator={ axes.y.tickFormatter }
disableAnimation
/>

<ChartAxis
type="bottom"
scale={ axis.x.scale }
scale={ axes.x.scale }
transform={ `translate(0, ${ innerHeight })` }
ticks={ axesConfig.x.ticks }
anchorEl={ overlayRef.current }
tickFormatGenerator={ axis.x.tickFormatter }
tickFormatGenerator={ axes.x.tickFormatter }
disableAnimation
/>

Expand All @@ -143,15 +143,15 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
width={ innerWidth }
tooltipWidth={ isGroupedValues ? 280 : 200 }
height={ innerHeight }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
xScale={ axes.x.scale }
yScale={ axes.y.scale }
data={ chartData }
/>

<ChartSelectionX
anchorEl={ overlayRef.current }
height={ innerHeight }
scale={ axis.x.scale }
scale={ axes.x.scale }
data={ chartData }
onSelect={ handleRangeSelect }
/>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions ui/shared/chart/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type TimeChartData = Array<TimeChartDataItem>;
export interface AxisConfig {
ticks?: number;
nice?: boolean;
noLabel?: boolean;
}

export interface AxesConfig {
Expand Down
28 changes: 13 additions & 15 deletions ui/shared/chart/useTimeChartController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { AxesConfig, ChartMargin, TimeChartData } from 'ui/shared/chart/typ
import useClientRect from 'lib/hooks/useClientRect';

import calculateInnerSize from './utils/calculateInnerSize';
import { getAxisParams, DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS } from './utils/timeChartAxis';
import { getAxesParams } from './utils/timeChartAxis';

interface Props {
data: TimeChartData;
Expand All @@ -19,29 +19,27 @@ export default function useTimeChartController({ data, margin, axesConfig }: Pro

// we need to recalculate the axis scale whenever the rect width changes
// eslint-disable-next-line react-hooks/exhaustive-deps
const axisParams = React.useMemo(() => getAxisParams(data, axesConfig), [ data, axesConfig, rect?.width ]);
const axesParams = React.useMemo(() => getAxesParams(data, axesConfig), [ data, axesConfig, rect?.width ]);

const chartMargin = React.useMemo(() => {
const exceedingDigits = (axisParams.y.labelFormatParams.maximumSignificantDigits ?? DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS) -
DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS;
const PIXELS_PER_DIGIT = 7;
const leftShift = PIXELS_PER_DIGIT * exceedingDigits;
const PIXELS_PER_DIGIT = 8;
const leftShift = axesConfig?.y?.noLabel ? 0 : PIXELS_PER_DIGIT * axesParams.y.labelFormatParams.maxLabelLength;

return {
...margin,
left: (margin?.left ?? 0) + leftShift,
};
}, [ axisParams.y.labelFormatParams.maximumSignificantDigits, margin ]);
}, [ axesParams.y.labelFormatParams.maxLabelLength, margin, axesConfig?.y?.noLabel ]);

const { innerWidth, innerHeight } = calculateInnerSize(rect, chartMargin);

const xScale = React.useMemo(() => {
return axisParams.x.scale.range([ 0, innerWidth ]);
}, [ axisParams.x.scale, innerWidth ]);
return axesParams.x.scale.range([ 0, innerWidth ]);
}, [ axesParams.x.scale, innerWidth ]);

const yScale = React.useMemo(() => {
return axisParams.y.scale.range([ innerHeight, 0 ]);
}, [ axisParams.y.scale, innerHeight ]);
return axesParams.y.scale.range([ innerHeight, 0 ]);
}, [ axesParams.y.scale, innerHeight ]);

return React.useMemo(() => {
return {
Expand All @@ -50,16 +48,16 @@ export default function useTimeChartController({ data, margin, axesConfig }: Pro
chartMargin,
innerWidth,
innerHeight,
axis: {
axes: {
x: {
tickFormatter: axisParams.x.tickFormatter,
tickFormatter: axesParams.x.tickFormatter,
scale: xScale,
},
y: {
tickFormatter: axisParams.y.tickFormatter,
tickFormatter: axesParams.y.tickFormatter,
scale: yScale,
},
},
};
}, [ axisParams.x.tickFormatter, axisParams.y.tickFormatter, chartMargin, innerHeight, innerWidth, rect, ref, xScale, yScale ]);
}, [ axesParams.x.tickFormatter, axesParams.y.tickFormatter, chartMargin, innerHeight, innerWidth, rect, ref, xScale, yScale ]);
}
28 changes: 15 additions & 13 deletions ui/shared/chart/utils/timeChartAxis.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as d3 from 'd3';
import _maxBy from 'lodash/maxBy';
import _unique from 'lodash/uniq';

import type { AxesConfig, AxisConfig, TimeChartData } from '../types';
Expand All @@ -8,8 +9,15 @@ import { WEEK, MONTH, YEAR } from 'lib/consts';
export const DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS = 2;
export const DEFAULT_MAXIMUM_FRACTION_DIGITS = 3;
export const MAXIMUM_SIGNIFICANT_DIGITS_LIMIT = 8;
export const DEFAULT_LABEL_LENGTH = 5;

export function getAxisParams(data: TimeChartData, axesConfig?: AxesConfig) {
export interface LabelFormatParams extends Intl.NumberFormatOptions {
maxLabelLength: number;
}

type Data = TimeChartData;

export function getAxesParams(data: Data, axesConfig?: AxesConfig) {
const { labelFormatParams: labelFormatParamsY, scale: yScale } = getAxisParamsY(data, axesConfig?.y);

return {
Expand All @@ -25,7 +33,7 @@ export function getAxisParams(data: TimeChartData, axesConfig?: AxesConfig) {
};
}

function getAxisParamsX(data: TimeChartData) {
function getAxisParamsX(data: Data) {
const min = d3.min(data, ({ items }) => d3.min(items, ({ date }) => date)) ?? new Date();
const max = d3.max(data, ({ items }) => d3.max(items, ({ date }) => date)) ?? new Date();
const scale = d3.scaleTime().domain([ min, max ]);
Expand Down Expand Up @@ -53,7 +61,7 @@ const tickFormatterX = (axis: d3.Axis<d3.NumberValue>) => (d: d3.AxisDomain) =>
return format(d as Date);
};

function getAxisParamsY(data: TimeChartData, config?: AxisConfig) {
function getAxisParamsY(data: Data, config?: AxisConfig) {
const DEFAULT_TICKS_NUM = 3;
const min = d3.min(data, ({ items }) => d3.min(items, ({ value }) => value)) ?? 0;
const max = d3.max(data, ({ items }) => d3.max(items, ({ value }) => value)) ?? 0;
Expand All @@ -72,27 +80,21 @@ function getAxisParamsY(data: TimeChartData, config?: AxisConfig) {

const getTickFormatterY = (params: Intl.NumberFormatOptions) => () => (d: d3.AxisDomain) => {
const num = Number(d);

if (num < 1) {
// for small number there are no algorithm to format label right now
// so we set it to 3 digits after dot maximum
return num.toLocaleString(undefined, { maximumFractionDigits: 3 });
}

return num.toLocaleString(undefined, params);
};

function getYLabelFormatParams(ticks: Array<number>, maximumSignificantDigits = DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS): Intl.NumberFormatOptions {
function getYLabelFormatParams(ticks: Array<number>, maximumSignificantDigits = DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS): LabelFormatParams {
const params = {
maximumFractionDigits: 3,
maximumFractionDigits: DEFAULT_MAXIMUM_FRACTION_DIGITS,
maximumSignificantDigits,
notation: 'compact' as const,
};

const uniqTicksStr = _unique(ticks.map((tick) => tick.toLocaleString(undefined, params)));
const maxLabelLength = _maxBy(uniqTicksStr, (items) => items.length)?.length ?? DEFAULT_LABEL_LENGTH;

if (uniqTicksStr.length === ticks.length || maximumSignificantDigits === MAXIMUM_SIGNIFICANT_DIGITS_LIMIT) {
return params;
return { ...params, maxLabelLength };
}

return getYLabelFormatParams(ticks, maximumSignificantDigits + 1);
Expand Down

0 comments on commit 8d6429d

Please sign in to comment.