Skip to content

Commit 8094fa3

Browse files
committed
Switch from auto configuration to manual trigger
1 parent 8bb6a9b commit 8094fa3

File tree

7 files changed

+284
-157
lines changed

7 files changed

+284
-157
lines changed

ui/packages/shared/hooks/src/useUserPreference/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ export const USER_PREFERENCES: {[key: string]: UserPreferenceDetails} = {
8080
description:
8181
'Choose how to align function names in the flame graph. Left alignment shows function names starting from the left edge, while right alignment shows them from the right edge.',
8282
},
83+
FLAMECHART_AUTO_CONFIG_POPOVER_DISMISSED: {
84+
name: 'Flamechart auto-configuration explanation dismissed',
85+
key: 'FLAMECHART_AUTO_CONFIG_POPOVER_DISMISSED',
86+
type: 'boolean',
87+
default: false,
88+
description:
89+
'When enabled, the flamechart auto-configuration explanation popover will not be shown.',
90+
},
8391
} as const;
8492

8593
export type UserPreference = keyof typeof USER_PREFERENCES;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2022 The Parca Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
import {Fragment, useState} from 'react';
15+
16+
import {Transition} from '@headlessui/react';
17+
import {Icon} from '@iconify/react';
18+
import {usePopper} from 'react-popper';
19+
20+
import {useParcaContext} from '@parca/components';
21+
22+
import {OPTIMAL_LABELS} from '../hooks/useAutoConfigureFlamechart';
23+
24+
interface FlameChartAutoConfigPopoverProps {
25+
isOpen: boolean;
26+
onDismiss: () => void;
27+
anchorRef: React.RefObject<HTMLElement>;
28+
currentSumByLabels: string[];
29+
}
30+
31+
export const FlameChartAutoConfigPopover = ({
32+
isOpen,
33+
onDismiss,
34+
anchorRef,
35+
currentSumByLabels,
36+
}: FlameChartAutoConfigPopoverProps): JSX.Element => {
37+
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
38+
const {flamechartHelpText} = useParcaContext();
39+
40+
const autoConfiguredLabels = OPTIMAL_LABELS.filter(label => !currentSumByLabels.includes(label));
41+
42+
const {styles, attributes} = usePopper(anchorRef.current, popperElement, {
43+
placement: 'bottom-start',
44+
strategy: 'absolute',
45+
modifiers: [
46+
{
47+
name: 'offset',
48+
options: {
49+
offset: [0, 8],
50+
},
51+
},
52+
{
53+
name: 'flip',
54+
options: {
55+
fallbackPlacements: ['top-start', 'bottom-end', 'top-end'],
56+
},
57+
},
58+
],
59+
});
60+
61+
if (!isOpen) return <></>;
62+
63+
return (
64+
<Transition
65+
show={isOpen}
66+
as={Fragment}
67+
enter="transition ease-out duration-200"
68+
enterFrom="opacity-0 translate-y-1"
69+
enterTo="opacity-100 translate-y-0"
70+
leave="transition ease-in duration-150"
71+
leaveFrom="opacity-100 translate-y-0"
72+
leaveTo="opacity-0 translate-y-1"
73+
>
74+
<div
75+
ref={setPopperElement}
76+
style={styles.popper}
77+
{...attributes.popper}
78+
className="z-50 w-96 rounded-lg bg-blue-50 dark:bg-gray-900 border border-blue-200 dark:border-gray-700 shadow-lg p-4"
79+
role="alert"
80+
aria-live="polite"
81+
aria-atomic="true"
82+
>
83+
{/* Close Button */}
84+
<button
85+
onClick={onDismiss}
86+
className="absolute top-4 right-2 text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-200"
87+
aria-label="Dismiss"
88+
>
89+
<Icon icon="mdi:close" width={20} height={20} />
90+
</button>
91+
92+
{/* Icon */}
93+
<div className="flex items-start gap-3">
94+
{/* Content */}
95+
<div className="flex-1 pr-6">
96+
<h3 className="text-sm font-semibold text-gray-900 dark:text-gray-100 mb-2">
97+
Flamechart Settings Auto-Configured
98+
</h3>
99+
<p className="text-sm text-gray-800 dark:text-gray-200 mb-3">
100+
We&apos;ve automatically adjusted your settings for optimal flamechart viewing:
101+
</p>
102+
<ul className="text-sm text-gray-800 dark:text-gray-200 mb-3 list-disc list-inside space-y-1">
103+
<li>Time range reduced to 1 minute</li>
104+
{autoConfiguredLabels.length > 0 && (
105+
<li>
106+
Added sum-by labels:{' '}
107+
{autoConfiguredLabels.map((label, index) => (
108+
<span key={label}>
109+
<code className="text-xs text-gray-200 dark:text-gray-800 bg-indigo-600 dark:bg-indigo-500 p-1 rounded">
110+
{label}
111+
</code>
112+
{index < autoConfiguredLabels.length - 1 ? ', ' : ''}
113+
</span>
114+
))}
115+
</li>
116+
)}
117+
</ul>
118+
{flamechartHelpText != null && (
119+
<p className="text-sm text-gray-800 dark:text-gray-200 [&_a]:underline">
120+
{flamechartHelpText}
121+
</p>
122+
)}
123+
</div>
124+
</div>
125+
</div>
126+
</Transition>
127+
);
128+
};

ui/packages/shared/profile/src/ProfileFlameGraph/index.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {useMeasure} from 'react-use';
1919

2020
import {FlamegraphArrow} from '@parca/client';
2121
import {
22+
Button,
2223
FlameGraphSkeleton,
2324
SandwichFlameGraphSkeleton,
2425
useParcaContext,
@@ -34,6 +35,7 @@ import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
3435
import {useProfileMetadata} from '../ProfileView/hooks/useProfileMetadata';
3536
import {useVisualizationState} from '../ProfileView/hooks/useVisualizationState';
3637
import {TimelineGuide} from '../TimelineGuide';
38+
import {useAutoConfigureFlamechart} from '../hooks/useAutoConfigureFlamechart';
3739
import {FlameGraphArrow} from './FlameGraphArrow';
3840
import {CurrentPathFrame, boundsFromProfileSource} from './FlameGraphArrow/utils';
3941

@@ -72,6 +74,12 @@ const ErrorContent = ({errorMessage}: {errorMessage: string | ReactNode}): JSX.E
7274
);
7375
};
7476

77+
const AutoConfigButton = ({onClick}: {onClick: () => void}): JSX.Element => (
78+
<Button onClick={onClick} variant="secondary" className="my-2">
79+
Auto-configure for Flamechart
80+
</Button>
81+
);
82+
7583
export const validateFlameChartQuery = (
7684
profileSource: MergedProfileSource
7785
): {isValid: boolean; isNonDelta: boolean; isDurationTooLong: boolean} => {
@@ -109,6 +117,8 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
109117
const [flameChartRef, {height: flameChartHeight}] = useMeasure();
110118
const {colorBy, setColorBy} = useVisualizationState();
111119

120+
const handleAutoConfigureFlameChart = useAutoConfigureFlamechart();
121+
112122
// Create local state for paths when in sandwich view to avoid URL updates
113123
const [localCurPathArrow, setLocalCurPathArrow] = useState<CurrentPathFrame[]>([]);
114124

@@ -232,24 +242,26 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
232242
return (
233243
<ErrorContent
234244
errorMessage={
235-
<>
245+
<div className="flex flex-col items-center">
236246
<span>
237247
Flame chart is unavailable for queries longer than one minute. Please select a
238248
point in the metrics graph to continue.
239249
</span>
250+
{!compareMode && <AutoConfigButton onClick={handleAutoConfigureFlameChart} />}
240251
{flamechartHelpText ?? null}
241-
</>
252+
</div>
242253
}
243254
/>
244255
);
245256
} else {
246257
return (
247258
<ErrorContent
248259
errorMessage={
249-
<>
260+
<div className="flex flex-col items-center">
250261
<span>The Flame chart is not available for this query.</span>
262+
{!compareMode && <AutoConfigButton onClick={handleAutoConfigureFlameChart} />}
251263
{flamechartHelpText ?? null}
252-
</>
264+
</div>
253265
}
254266
/>
255267
);
@@ -326,6 +338,8 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
326338
mappingsList,
327339
filenamesList,
328340
colorBy,
341+
handleAutoConfigureFlameChart,
342+
compareMode,
329343
]);
330344

331345
useEffect(() => {

ui/packages/shared/profile/src/ProfileSelector/index.tsx

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import {useLabelNames} from '../hooks/useLabels';
3737
import {useQueryState} from '../hooks/useQueryState';
3838
import useGrpcQuery from '../useGrpcQuery';
3939
import {MetricsGraphSection} from './MetricsGraphSection';
40-
import {useAutoFlameChartQuerySelector} from './useAutoFlameChartQuerySelector';
4140
import {useAutoQuerySelector} from './useAutoQuerySelector';
4241

4342
export interface QuerySelection {
@@ -119,9 +118,6 @@ const ProfileSelector = ({
119118
const {viewComponent} = useParcaContext();
120119
const [queryBrowserMode, setQueryBrowserMode] = useURLState('query_browser_mode');
121120
const batchUpdates = useURLStateBatch();
122-
const [dashboardItems] = useURLState<string[]>('dashboard_items', {
123-
alwaysReturnArray: true,
124-
});
125121

126122
// Use the new useQueryState hook - reads directly from URL params
127123
const {
@@ -150,6 +146,16 @@ const ProfileSelector = ({
150146
queryBrowserMode === 'advanced'
151147
);
152148

149+
// Sync local timeRangeSelection state when draftSelection changes from external URL updates
150+
useEffect(() => {
151+
const newTimeRange = DateTimeRange.fromRangeKey(
152+
draftSelection.timeSelection,
153+
draftSelection.from,
154+
draftSelection.to
155+
);
156+
setTimeRangeSelection(newTimeRange);
157+
}, [draftSelection.timeSelection, draftSelection.from, draftSelection.to]);
158+
153159
// Handler to update draft when time range changes
154160
const handleTimeRangeChange = useCallback(
155161
(range: DateTimeRange) => {
@@ -260,19 +266,6 @@ const ProfileSelector = ({
260266
loading: sumByLoading,
261267
});
262268

263-
useAutoFlameChartQuerySelector({
264-
queryClient,
265-
dashboardItems: dashboardItems ?? [],
266-
profileType,
267-
timeRange: timeRangeSelection,
268-
comparing,
269-
loading: sumByLoading || profileTypesLoading,
270-
setTimeRangeSelection,
271-
setDraftTimeRange,
272-
setDraftSumBy,
273-
commitDraft,
274-
});
275-
276269
const searchDisabled =
277270
queryExpressionString === undefined ||
278271
queryExpressionString === '' ||

0 commit comments

Comments
 (0)