mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
(fix) Dont crash when rendering numerical group names in line charts (#199)
This commit is contained in:
parent
f4360edf39
commit
4ee544cd08
5 changed files with 91 additions and 52 deletions
5
.changeset/hot-pens-happen.md
Normal file
5
.changeset/hot-pens-happen.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@hyperdx/app': patch
|
||||
---
|
||||
|
||||
Fix: Don't crash line chart when rendering numerical group values
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
({
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)}`;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue