Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better labels for graphs #1348

Merged
merged 4 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions deploy/values/review/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ frontend:
NEXT_PUBLIC_FEATURED_NETWORKS: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_LOGO: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/goerli.svg
NEXT_PUBLIC_NETWORK_ICON: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/goerli.svg
NEXT_PUBLIC_API_HOST: blockscout-main.k8s-dev.blockscout.com
NEXT_PUBLIC_STATS_API_HOST: https://stats-test.k8s-dev.blockscout.com/
NEXT_PUBLIC_API_HOST: etc.blockscout.com
NEXT_PUBLIC_STATS_API_HOST: https://stats-etc.k8s.blockscout.com/
NEXT_PUBLIC_VISUALIZE_API_HOST: http://visualizer-svc.visualizer-testing.svc.cluster.local/
NEXT_PUBLIC_CONTRACT_INFO_API_HOST: https://contracts-info-test.k8s-dev.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST: https://admin-rs-test.k8s-dev.blockscout.com
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.
29 changes: 16 additions & 13 deletions ui/home/indicators/ChainIndicatorChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import React from 'react';

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

import useClientRect from 'lib/hooks/useClientRect';
import ChartArea from 'ui/shared/chart/ChartArea';
import ChartLine from 'ui/shared/chart/ChartLine';
import ChartOverlay from 'ui/shared/chart/ChartOverlay';
import ChartTooltip from 'ui/shared/chart/ChartTooltip';
import useTimeChartController from 'ui/shared/chart/useTimeChartController';
import calculateInnerSize from 'ui/shared/chart/utils/calculateInnerSize';

interface Props {
data: TimeChartData;
Expand All @@ -22,26 +20,31 @@ const ChainIndicatorChart = ({ data }: Props) => {
const overlayRef = React.useRef<SVGRectElement>(null);
const lineColor = useToken('colors', 'blue.500');

const [ rect, ref ] = useClientRect<SVGSVGElement>();
const { innerWidth, innerHeight } = calculateInnerSize(rect, CHART_MARGIN);
const { xScale, yScale } = useTimeChartController({
const axesConfig = React.useMemo(() => {
return {
x: { ticks: 4 },
y: { ticks: 3, nice: true },
};
}, [ ]);

const { rect, ref, axis, innerWidth, innerHeight } = useTimeChartController({
data,
width: innerWidth,
height: innerHeight,
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 }>
<ChartArea
data={ data[0].items }
xScale={ xScale }
yScale={ yScale }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
/>
<ChartLine
data={ data[0].items }
xScale={ xScale }
yScale={ yScale }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
stroke={ lineColor }
animation="left"
strokeWidth={ 3 }
Expand All @@ -51,8 +54,8 @@ const ChainIndicatorChart = ({ data }: Props) => {
anchorEl={ overlayRef.current }
width={ innerWidth }
height={ innerHeight }
xScale={ xScale }
yScale={ yScale }
xScale={ axis.x.scale }
yScale={ axis.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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 47 additions & 31 deletions ui/shared/chart/ChartWidgetGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useToken } from '@chakra-ui/react';
import * as d3 from 'd3';
import React, { useEffect, useMemo } from 'react';
import React from 'react';

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

import dayjs from 'lib/date/dayjs';
import useClientRect from 'lib/hooks/useClientRect';
import useIsMobile from 'lib/hooks/useIsMobile';
import ChartArea from 'ui/shared/chart/ChartArea';
import ChartAxis from 'ui/shared/chart/ChartAxis';
Expand All @@ -15,7 +14,6 @@ import ChartOverlay from 'ui/shared/chart/ChartOverlay';
import ChartSelectionX from 'ui/shared/chart/ChartSelectionX';
import ChartTooltip from 'ui/shared/chart/ChartTooltip';
import useTimeChartController from 'ui/shared/chart/useTimeChartController';
import calculateInnerSize from 'ui/shared/chart/utils/calculateInnerSize';

interface Props {
isEnlarged?: boolean;
Expand All @@ -31,44 +29,62 @@ interface Props {
const MAX_SHOW_ITEMS = 100_000_000_000;
const DEFAULT_CHART_MARGIN = { bottom: 20, left: 40, right: 20, top: 10 };

const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title, margin, units }: Props) => {
const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title, margin: marginProps, units }: Props) => {
const isMobile = useIsMobile();
const color = useToken('colors', 'blue.200');
const overlayRef = React.useRef<SVGRectElement>(null);
const chartId = `chart-${ title.split(' ').join('') }-${ isEnlarged ? 'fullscreen' : 'small' }`;

const [ rect, ref ] = useClientRect<SVGSVGElement>();
const chartMargin = { ...DEFAULT_CHART_MARGIN, ...margin };
const { innerWidth, innerHeight } = calculateInnerSize(rect, chartMargin);
const overlayRef = React.useRef<SVGRectElement>(null);

const chartId = `chart-${ title.split(' ').join('') }-${ isEnlarged ? 'fullscreen' : 'small' }`;
const [ range, setRange ] = React.useState<[ Date, Date ]>([ items[0].date, items[items.length - 1].date ]);

const rangedItems = useMemo(() =>
const rangedItems = React.useMemo(() =>
items.filter((item) => item.date >= range[0] && item.date <= range[1]),
[ items, range ]);
const isGroupedValues = rangedItems.length > MAX_SHOW_ITEMS;

const displayedData = useMemo(() => {
const displayedData = React.useMemo(() => {
if (isGroupedValues) {
return groupChartItemsByWeekNumber(rangedItems);
} else {
return rangedItems;
}
}, [ isGroupedValues, rangedItems ]);

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

const { xTickFormat, yTickFormat, xScale, yScale } = useTimeChartController({
data: [ { items: displayedData, name: title, color } ],
width: innerWidth,
height: innerHeight,
const margin: ChartMargin = React.useMemo(() => ({ ...DEFAULT_CHART_MARGIN, ...marginProps }), [ marginProps ]);
const axesConfig = React.useMemo(() => {
return {
x: {
ticks: isEnlarged ? 8 : 4,
},
y: {
ticks: isEnlarged ? 6 : 3,
nice: true,
},
};
}, [ isEnlarged ]);

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

const handleRangeSelect = React.useCallback((nextRange: [ Date, Date ]) => {
setRange([ nextRange[0], nextRange[1] ]);
onZoom();
}, [ onZoom ]);

useEffect(() => {
React.useEffect(() => {
if (isZoomResetInitial) {
setRange([ items[0].date, items[items.length - 1].date ]);
}
Expand All @@ -80,8 +96,8 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
<g transform={ `translate(${ chartMargin?.left || 0 },${ chartMargin?.top || 0 })` }>
<ChartGridLine
type="horizontal"
scale={ yScale }
ticks={ isEnlarged ? 6 : 3 }
scale={ axis.y.scale }
ticks={ axesConfig.y.ticks }
size={ innerWidth }
disableAnimation
/>
Expand All @@ -90,34 +106,34 @@ const ChartWidgetGraph = ({ isEnlarged, items, onZoom, isZoomResetInitial, title
id={ chartId }
data={ displayedData }
color={ color }
xScale={ xScale }
yScale={ yScale }
xScale={ axis.x.scale }
yScale={ axis.y.scale }
/>

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

<ChartAxis
type="left"
scale={ yScale }
ticks={ isEnlarged ? 6 : 3 }
tickFormatGenerator={ yTickFormat }
scale={ axis.y.scale }
ticks={ axesConfig.y.ticks }
tickFormatGenerator={ axis.y.tickFormatter }
disableAnimation
/>

<ChartAxis
type="bottom"
scale={ xScale }
scale={ axis.x.scale }
transform={ `translate(0, ${ innerHeight })` }
ticks={ isMobile ? 1 : 4 }
ticks={ axesConfig.x.ticks }
anchorEl={ overlayRef.current }
tickFormatGenerator={ xTickFormat }
tickFormatGenerator={ axis.x.tickFormatter }
disableAnimation
/>

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

<ChartSelectionX
anchorEl={ overlayRef.current }
height={ innerHeight }
scale={ xScale }
scale={ axis.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.
10 changes: 10 additions & 0 deletions ui/shared/chart/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ export interface TimeChartDataItem {
}

export type TimeChartData = Array<TimeChartDataItem>;

export interface AxisConfig {
ticks?: number;
nice?: boolean;
}

export interface AxesConfig {
x?: AxisConfig;
y?: AxisConfig;
}
Loading
Loading