fix: Fix crash when navigating away from chart explorer search page (#1278)

This commit is contained in:
Drew Davis 2025-10-17 11:16:47 -04:00 committed by GitHub
parent 065cabdb47
commit 6262ced8ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 16 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---
fix: Fix crash when navigating away from chart explorer search page

View file

@ -7,6 +7,7 @@ import {
useRef,
useState,
} from 'react';
import { omit } from 'lodash';
import {
Control,
Controller,
@ -385,6 +386,10 @@ export type SavedChartConfigWithSelectArray = Omit<
select: Exclude<SavedChartConfig['select'], string>;
};
type SavedChartConfigWithSeries = SavedChartConfig & {
series: SavedChartConfigWithSelectArray['select'];
};
export default function EditTimeChartForm({
dashboardId,
chartConfig,
@ -414,10 +419,20 @@ export default function EditTimeChartForm({
'data-testid'?: string;
submitRef?: React.MutableRefObject<(() => void) | undefined>;
}) {
// useFieldArray only supports array type fields, and select can be either a string or array.
// To solve for this, we maintain an extra form field called 'series' which is always an array.
const configWithSeries: SavedChartConfigWithSeries = useMemo(
() => ({
...chartConfig,
series: Array.isArray(chartConfig.select) ? chartConfig.select : [],
}),
[chartConfig],
);
const { control, watch, setValue, handleSubmit, register } =
useForm<SavedChartConfig>({
defaultValues: chartConfig,
values: chartConfig,
useForm<SavedChartConfigWithSeries>({
defaultValues: configWithSeries,
values: configWithSeries,
resolver: zodResolver(zSavedChartConfig),
});
@ -427,8 +442,8 @@ export default function EditTimeChartForm({
remove: removeSeries,
swap: swapSeries,
} = useFieldArray({
control: control as Control<SavedChartConfigWithSelectArray>,
name: 'select',
control: control as Control<SavedChartConfigWithSeries>,
name: 'series',
});
const select = watch('select');
@ -492,11 +507,18 @@ export default function EditTimeChartForm({
const onSubmit = useCallback(() => {
handleSubmit(form => {
setChartConfig(form);
// Merge the series and select fields back together, and prevent the series field from being submitted
const config = {
...omit(form, ['series']),
select:
form.displayType === DisplayType.Search ? form.select : form.series,
};
setChartConfig(config);
if (tableSource != null) {
const isSelectEmpty = !form.select || form.select.length === 0; // select is string or array
const isSelectEmpty = !config.select || config.select.length === 0; // select is string or array
const newConfig = {
...form,
...config,
from: tableSource.from,
timestampValueExpression: tableSource.timestampValueExpression,
dateRange,
@ -505,7 +527,7 @@ export default function EditTimeChartForm({
metricTables: tableSource.metricTables,
select: isSelectEmpty
? tableSource.defaultTableSelectExpression || ''
: form.select,
: config.select,
};
setQueriedConfig(
// WARNING: DON'T JUST ASSIGN OBJECTS OR DO SPREAD OPERATOR STUFF WHEN
@ -525,12 +547,15 @@ export default function EditTimeChartForm({
}, [onSubmit, submitRef]);
const handleSave = useCallback(
(v: SavedChartConfig) => {
(v: SavedChartConfigWithSeries) => {
// If the chart type is search, we need to ensure the select is a string
if (displayType === DisplayType.Search && typeof v.select !== 'string') {
v.select = '';
} else if (displayType !== DisplayType.Search) {
v.select = v.series;
}
onSave?.(v);
// Avoid saving the series field. Series should be persisted in the select field.
onSave?.(omit(v, ['series']));
},
[onSave, displayType],
);
@ -543,17 +568,20 @@ export default function EditTimeChartForm({
if (name === 'displayType' && type === 'change') {
if (_.displayType === DisplayType.Search && typeof select !== 'string') {
setValue('select', '');
setValue('series', []);
}
if (_.displayType !== DisplayType.Search && typeof select === 'string') {
setValue('where', '');
setValue('select', [
const defaultSeries: SavedChartConfigWithSelectArray['select'] = [
{
aggFn: 'count',
aggCondition: '',
aggConditionLanguage: 'lucene',
valueExpression: '',
},
]);
];
setValue('where', '');
setValue('select', defaultSeries);
setValue('series', defaultSeries);
}
onSubmit();
}
@ -711,7 +739,7 @@ export default function EditTimeChartForm({
index={index}
key={field.id}
parentRef={parentRef}
namePrefix={`select.${index}.`}
namePrefix={`series.${index}.`}
onRemoveSeries={removeSeries}
length={fields.length}
onSwapSeries={swapSeries}
@ -773,7 +801,7 @@ export default function EditTimeChartForm({
Add Series
</Button>
)}
{select.length == 2 && displayType !== DisplayType.Number && (
{fields.length == 2 && displayType !== DisplayType.Number && (
<Switch
label="As Ratio"
size="sm"