(fix) Dont crash when rendering numerical group names in line charts (#199)

This commit is contained in:
Mike Shi 2024-01-07 18:02:28 -08:00 committed by GitHub
parent f4360edf39
commit 4ee544cd08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 52 deletions

View file

@ -0,0 +1,5 @@
---
'@hyperdx/app': patch
---
Fix: Don't crash line chart when rendering numerical group values

View file

@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import Head from 'next/head';
import produce from 'immer';
import { Button } from 'react-bootstrap';
import { ErrorBoundary } from 'react-error-boundary';
import { toast } from 'react-toastify';
import type { QueryParamConfig } from 'serialize-query-params';
import { decodeArray, encodeArray } from 'serialize-query-params';
@ -286,7 +287,16 @@ export default function GraphPage() {
className="w-100 mt-4 flex-grow-1"
style={{ height: 400, minWidth: 0 }}
>
{chartConfig != null && <HDXLineChart config={chartConfig} />}
<ErrorBoundary
onError={console.error}
fallback={
<div className="text-danger px-2 py-1 m-2 fs-7 font-monospace bg-danger-transparent">
An error occurred while rendering the chart.
</div>
}
>
{chartConfig != null && <HDXLineChart config={chartConfig} />}
</ErrorBoundary>
</div>
{chartConfig != null && chartConfig.table === 'logs' && (
<div className="ps-2 mt-2 border-top border-dark">

View file

@ -12,6 +12,7 @@ import Link from 'next/link';
import { useRouter } from 'next/router';
import produce from 'immer';
import { Button, Form, Modal } from 'react-bootstrap';
import { ErrorBoundary } from 'react-error-boundary';
import RGL, { WidthProvider } from 'react-grid-layout';
import { useHotkeys } from 'react-hotkeys-hook';
import { useQueryClient } from 'react-query';
@ -287,31 +288,42 @@ const Tile = forwardRef(
className="fs-7 text-muted flex-grow-1 overflow-hidden"
onMouseDown={e => e.stopPropagation()}
>
{chart.series[0].type === 'time' && config.type === 'time' && (
<HDXMultiSeriesLineChart config={config} />
)}
{chart.series[0].type === 'table' && config.type === 'table' && (
<HDXMultiSeriesTableChart config={config} />
)}
{config.type === 'histogram' && (
<HDXHistogramChart config={config} onSettled={onSettled} />
)}
{config.type === 'markdown' && <HDXMarkdownChart config={config} />}
{config.type === 'number' && (
<HDXNumberChart config={config} onSettled={onSettled} />
)}
{config.type === 'search' && (
<div style={{ height: '100%' }}>
<LogTableWithSidePanel
config={config}
isLive={false}
isUTC={false}
setIsUTC={() => {}}
onPropertySearchClick={() => {}}
onSettled={onSettled}
/>
</div>
)}
<ErrorBoundary
onError={console.error}
fallback={
<div className="text-danger px-2 py-1 m-2 fs-7 font-monospace bg-danger-transparent">
An error occurred while rendering the chart.
</div>
}
>
{chart.series[0].type === 'time' && config.type === 'time' && (
<HDXMultiSeriesLineChart config={config} />
)}
{chart.series[0].type === 'table' && config.type === 'table' && (
<HDXMultiSeriesTableChart config={config} />
)}
{config.type === 'histogram' && (
<HDXHistogramChart config={config} onSettled={onSettled} />
)}
{config.type === 'markdown' && (
<HDXMarkdownChart config={config} />
)}
{config.type === 'number' && (
<HDXNumberChart config={config} onSettled={onSettled} />
)}
{config.type === 'search' && (
<div style={{ height: '100%' }}>
<LogTableWithSidePanel
config={config}
isLive={false}
isUTC={false}
setIsUTC={() => {}}
onPropertySearchClick={() => {}}
onSettled={onSettled}
/>
</div>
)}
</ErrorBoundary>
</div>
)}
{children}

View file

@ -3,6 +3,7 @@ import Link from 'next/link';
import cx from 'classnames';
import { add, format } from 'date-fns';
import pick from 'lodash/pick';
import { ErrorBoundary, withErrorBoundary } from 'react-error-boundary';
import { toast } from 'react-toastify';
import {
Bar,
@ -289,31 +290,41 @@ const MemoChart = memo(function MemoChart({
);
});
export const HDXLineChartTooltip = memo((props: any) => {
const timeFormat: TimeFormat = useUserPreferences().timeFormat;
const tsFormat = TIME_TOKENS[timeFormat];
const { active, payload, label, numberFormat } = props;
if (active && payload && payload.length) {
return (
<div className={styles.chartTooltip}>
<div className={styles.chartTooltipHeader}>
{format(new Date(label * 1000), tsFormat)}
</div>
<div className={styles.chartTooltipContent}>
{payload
.sort((a: any, b: any) => b.value - a.value)
.map((p: any) => (
<div key={p.dataKey} style={{ color: p.color }}>
{truncateMiddle(p.name ?? p.dataKey, 70)}:{' '}
{numberFormat ? formatNumber(p.value, numberFormat) : p.value}
</div>
))}
export const HDXLineChartTooltip = withErrorBoundary(
memo((props: any) => {
const timeFormat: TimeFormat = useUserPreferences().timeFormat;
const tsFormat = TIME_TOKENS[timeFormat];
const { active, payload, label, numberFormat } = props;
if (active && payload && payload.length) {
return (
<div className={styles.chartTooltip}>
<div className={styles.chartTooltipHeader}>
{format(new Date(label * 1000), tsFormat)}
</div>
<div className={styles.chartTooltipContent}>
{payload
.sort((a: any, b: any) => b.value - a.value)
.map((p: any) => (
<div key={p.dataKey} style={{ color: p.color }}>
{truncateMiddle(p.name ?? p.dataKey, 70)}:{' '}
{numberFormat ? formatNumber(p.value, numberFormat) : p.value}
</div>
))}
</div>
</div>
);
}
return null;
}),
{
onError: console.error,
fallback: (
<div className="text-danger px-2 py-1 m-2 fs-8 font-monospace bg-danger-transparent">
An error occurred while rendering the tooltip.
</div>
);
}
return null;
});
),
},
);
const HDXLineChart = memo(
({

View file

@ -308,10 +308,11 @@ export const semanticKeyedColor = (key: string | number | undefined) => {
};
export const truncateMiddle = (str: string, maxLen = 10) => {
if (str.length <= maxLen) {
return str;
const coercedStr = `${str}`;
if (coercedStr.length <= maxLen) {
return coercedStr;
}
return `${str.slice(0, (maxLen - 2) / 2)}..${str.slice(
return `${coercedStr.slice(0, (maxLen - 2) / 2)}..${coercedStr.slice(
(-1 * (maxLen - 2)) / 2,
)}`;
};