Skip to content

Commit 6262ced

Browse files
authored
fix: Fix crash when navigating away from chart explorer search page (#1278)
1 parent 065cabd commit 6262ced

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

.changeset/nasty-fans-run.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
fix: Fix crash when navigating away from chart explorer search page

packages/app/src/components/DBEditTimeChartForm.tsx

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
useRef,
88
useState,
99
} from 'react';
10+
import { omit } from 'lodash';
1011
import {
1112
Control,
1213
Controller,
@@ -385,6 +386,10 @@ export type SavedChartConfigWithSelectArray = Omit<
385386
select: Exclude<SavedChartConfig['select'], string>;
386387
};
387388

389+
type SavedChartConfigWithSeries = SavedChartConfig & {
390+
series: SavedChartConfigWithSelectArray['select'];
391+
};
392+
388393
export default function EditTimeChartForm({
389394
dashboardId,
390395
chartConfig,
@@ -414,10 +419,20 @@ export default function EditTimeChartForm({
414419
'data-testid'?: string;
415420
submitRef?: React.MutableRefObject<(() => void) | undefined>;
416421
}) {
422+
// useFieldArray only supports array type fields, and select can be either a string or array.
423+
// To solve for this, we maintain an extra form field called 'series' which is always an array.
424+
const configWithSeries: SavedChartConfigWithSeries = useMemo(
425+
() => ({
426+
...chartConfig,
427+
series: Array.isArray(chartConfig.select) ? chartConfig.select : [],
428+
}),
429+
[chartConfig],
430+
);
431+
417432
const { control, watch, setValue, handleSubmit, register } =
418-
useForm<SavedChartConfig>({
419-
defaultValues: chartConfig,
420-
values: chartConfig,
433+
useForm<SavedChartConfigWithSeries>({
434+
defaultValues: configWithSeries,
435+
values: configWithSeries,
421436
resolver: zodResolver(zSavedChartConfig),
422437
});
423438

@@ -427,8 +442,8 @@ export default function EditTimeChartForm({
427442
remove: removeSeries,
428443
swap: swapSeries,
429444
} = useFieldArray({
430-
control: control as Control<SavedChartConfigWithSelectArray>,
431-
name: 'select',
445+
control: control as Control<SavedChartConfigWithSeries>,
446+
name: 'series',
432447
});
433448

434449
const select = watch('select');
@@ -492,11 +507,18 @@ export default function EditTimeChartForm({
492507

493508
const onSubmit = useCallback(() => {
494509
handleSubmit(form => {
495-
setChartConfig(form);
510+
// Merge the series and select fields back together, and prevent the series field from being submitted
511+
const config = {
512+
...omit(form, ['series']),
513+
select:
514+
form.displayType === DisplayType.Search ? form.select : form.series,
515+
};
516+
517+
setChartConfig(config);
496518
if (tableSource != null) {
497-
const isSelectEmpty = !form.select || form.select.length === 0; // select is string or array
519+
const isSelectEmpty = !config.select || config.select.length === 0; // select is string or array
498520
const newConfig = {
499-
...form,
521+
...config,
500522
from: tableSource.from,
501523
timestampValueExpression: tableSource.timestampValueExpression,
502524
dateRange,
@@ -505,7 +527,7 @@ export default function EditTimeChartForm({
505527
metricTables: tableSource.metricTables,
506528
select: isSelectEmpty
507529
? tableSource.defaultTableSelectExpression || ''
508-
: form.select,
530+
: config.select,
509531
};
510532
setQueriedConfig(
511533
// WARNING: DON'T JUST ASSIGN OBJECTS OR DO SPREAD OPERATOR STUFF WHEN
@@ -525,12 +547,15 @@ export default function EditTimeChartForm({
525547
}, [onSubmit, submitRef]);
526548

527549
const handleSave = useCallback(
528-
(v: SavedChartConfig) => {
550+
(v: SavedChartConfigWithSeries) => {
529551
// If the chart type is search, we need to ensure the select is a string
530552
if (displayType === DisplayType.Search && typeof v.select !== 'string') {
531553
v.select = '';
554+
} else if (displayType !== DisplayType.Search) {
555+
v.select = v.series;
532556
}
533-
onSave?.(v);
557+
// Avoid saving the series field. Series should be persisted in the select field.
558+
onSave?.(omit(v, ['series']));
534559
},
535560
[onSave, displayType],
536561
);
@@ -543,17 +568,20 @@ export default function EditTimeChartForm({
543568
if (name === 'displayType' && type === 'change') {
544569
if (_.displayType === DisplayType.Search && typeof select !== 'string') {
545570
setValue('select', '');
571+
setValue('series', []);
546572
}
547573
if (_.displayType !== DisplayType.Search && typeof select === 'string') {
548-
setValue('where', '');
549-
setValue('select', [
574+
const defaultSeries: SavedChartConfigWithSelectArray['select'] = [
550575
{
551576
aggFn: 'count',
552577
aggCondition: '',
553578
aggConditionLanguage: 'lucene',
554579
valueExpression: '',
555580
},
556-
]);
581+
];
582+
setValue('where', '');
583+
setValue('select', defaultSeries);
584+
setValue('series', defaultSeries);
557585
}
558586
onSubmit();
559587
}
@@ -711,7 +739,7 @@ export default function EditTimeChartForm({
711739
index={index}
712740
key={field.id}
713741
parentRef={parentRef}
714-
namePrefix={`select.${index}.`}
742+
namePrefix={`series.${index}.`}
715743
onRemoveSeries={removeSeries}
716744
length={fields.length}
717745
onSwapSeries={swapSeries}
@@ -773,7 +801,7 @@ export default function EditTimeChartForm({
773801
Add Series
774802
</Button>
775803
)}
776-
{select.length == 2 && displayType !== DisplayType.Number && (
804+
{fields.length == 2 && displayType !== DisplayType.Number && (
777805
<Switch
778806
label="As Ratio"
779807
size="sm"

0 commit comments

Comments
 (0)