@parca/profile 0.19.139 → 0.19.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/GraphTooltipArrow/Content.js +224 -30
- package/dist/GraphTooltipArrow/DockedGraphTooltip/index.js +192 -33
- package/dist/GraphTooltipArrow/ExpandOnHoverValue.js +53 -3
- package/dist/GraphTooltipArrow/index.d.ts.map +1 -1
- package/dist/GraphTooltipArrow/index.js +86 -56
- package/dist/GraphTooltipArrow/useGraphTooltip/index.js +37 -37
- package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +103 -73
- package/dist/MatchersInput/SuggestionItem.js +91 -12
- package/dist/MatchersInput/SuggestionsList.d.ts +2 -1
- package/dist/MatchersInput/SuggestionsList.d.ts.map +1 -1
- package/dist/MatchersInput/SuggestionsList.js +371 -157
- package/dist/MatchersInput/SuggestionsList.test.d.ts +2 -0
- package/dist/MatchersInput/SuggestionsList.test.d.ts.map +1 -0
- package/dist/MatchersInput/index.js +308 -115
- package/dist/MetricsCircle/index.js +39 -3
- package/dist/MetricsGraph/MetricsContextMenu/index.js +119 -19
- package/dist/MetricsGraph/MetricsInfoPanel/index.js +81 -20
- package/dist/MetricsGraph/MetricsTooltip/index.d.ts.map +1 -1
- package/dist/MetricsGraph/MetricsTooltip/index.js +107 -74
- package/dist/MetricsGraph/index.js +552 -203
- package/dist/MetricsGraph/useMetricsGraphDimensions.js +46 -25
- package/dist/MetricsGraph/utils/colorMapping.js +24 -17
- package/dist/MetricsSeries/index.js +70 -7
- package/dist/PreSelectedMatchers/index.d.ts.map +1 -1
- package/dist/PreSelectedMatchers/index.js +249 -102
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +240 -49
- package/dist/ProfileExplorer/ProfileExplorerSingle.js +98 -11
- package/dist/ProfileExplorer/index.js +183 -32
- package/dist/ProfileFlameChart/SamplesStrips/SamplesGraph/index.js +333 -148
- package/dist/ProfileFlameChart/SamplesStrips/SamplesStrips.stories.js +69 -35
- package/dist/ProfileFlameChart/SamplesStrips/index.js +645 -134
- package/dist/ProfileFlameChart/SamplesStrips/labelSetUtils.js +114 -55
- package/dist/ProfileFlameChart/index.js +266 -134
- package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +287 -88
- package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenuWrapper.js +56 -20
- package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.js +211 -140
- package/dist/ProfileFlameGraph/FlameGraphArrow/MemoizedTooltip.js +133 -38
- package/dist/ProfileFlameGraph/FlameGraphArrow/MiniMap.js +261 -216
- package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.js +71 -45
- package/dist/ProfileFlameGraph/FlameGraphArrow/TooltipContext.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/TooltipContext.js +58 -28
- package/dist/ProfileFlameGraph/FlameGraphArrow/ZoomControls.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/ZoomControls.js +59 -8
- package/dist/ProfileFlameGraph/FlameGraphArrow/index.js +396 -179
- package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.d.ts.map +1 -1
- package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.js +68 -50
- package/dist/ProfileFlameGraph/FlameGraphArrow/useMappingList.js +62 -38
- package/dist/ProfileFlameGraph/FlameGraphArrow/useNodeColor.js +14 -6
- package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.js +124 -82
- package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.js +160 -98
- package/dist/ProfileFlameGraph/FlameGraphArrow/useZoom.js +232 -112
- package/dist/ProfileFlameGraph/FlameGraphArrow/utils.js +137 -114
- package/dist/ProfileFlameGraph/benchmarks/benchdata/populateData.js +85 -0
- package/dist/ProfileFlameGraph/index.js +324 -148
- package/dist/ProfileMetricsGraph/hooks/useQueryRange.js +140 -32
- package/dist/ProfileMetricsGraph/index.js +518 -259
- package/dist/ProfileSelector/CompareButton.js +132 -12
- package/dist/ProfileSelector/MetricsGraphSection.js +234 -67
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +730 -142
- package/dist/ProfileSelector/useAutoQuerySelector.js +249 -130
- package/dist/ProfileSource.js +230 -163
- package/dist/ProfileTypeSelector/index.js +214 -125
- package/dist/ProfileView/components/ActionButtons/GroupByDropdown.js +50 -4
- package/dist/ProfileView/components/ActionButtons/SortByDropdown.js +139 -33
- package/dist/ProfileView/components/ColorStackLegend.js +184 -55
- package/dist/ProfileView/components/DashboardItems/index.js +87 -28
- package/dist/ProfileView/components/DashboardLayout/index.js +108 -16
- package/dist/ProfileView/components/DiffLegend.js +172 -29
- package/dist/ProfileView/components/GroupByLabelsDropdown/index.js +199 -55
- package/dist/ProfileView/components/InvertCallStack/index.js +99 -10
- package/dist/ProfileView/components/ProfileFilters/filterPresets.js +260 -315
- package/dist/ProfileView/components/ProfileFilters/index.js +518 -215
- package/dist/ProfileView/components/ProfileFilters/useProfileFilters.js +370 -306
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +188 -120
- package/dist/ProfileView/components/ProfileHeader/index.js +105 -11
- package/dist/ProfileView/components/ShareButton/ResultBox.js +119 -16
- package/dist/ProfileView/components/ShareButton/index.js +352 -62
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
- package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +675 -195
- package/dist/ProfileView/components/Toolbars/SwitchMenuItem.js +94 -7
- package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +198 -157
- package/dist/ProfileView/components/Toolbars/index.js +441 -21
- package/dist/ProfileView/components/ViewSelector/Dropdown.js +233 -22
- package/dist/ProfileView/components/ViewSelector/index.js +211 -91
- package/dist/ProfileView/components/VisualizationContainer/index.d.ts.map +1 -1
- package/dist/ProfileView/components/VisualizationContainer/index.js +52 -7
- package/dist/ProfileView/components/VisualizationPanel.js +185 -8
- package/dist/ProfileView/context/DashboardContext.js +84 -28
- package/dist/ProfileView/context/ProfileViewContext.js +56 -15
- package/dist/ProfileView/hooks/useAutoSelectDimension.js +71 -41
- package/dist/ProfileView/hooks/useProfileMetadata.js +50 -18
- package/dist/ProfileView/hooks/useResetFlameGraphState.js +31 -10
- package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.js +72 -29
- package/dist/ProfileView/hooks/useResetStateOnSeriesChange.js +39 -13
- package/dist/ProfileView/hooks/useVisualizationState.js +262 -87
- package/dist/ProfileView/index.js +383 -45
- package/dist/ProfileView/types/visualization.js +1 -13
- package/dist/ProfileView/utils/colorUtils.js +8 -7
- package/dist/ProfileViewWithData.js +332 -237
- package/dist/QueryControls/index.js +418 -47
- package/dist/Sandwich/components/CalleesSection.js +54 -4
- package/dist/Sandwich/components/CallersSection.js +97 -27
- package/dist/Sandwich/components/TableSection.js +77 -4
- package/dist/Sandwich/index.js +125 -12
- package/dist/Sandwich/utils/processRowData.js +48 -39
- package/dist/SelectWithRefresh/index.js +102 -28
- package/dist/SimpleMatchers/Select.js +520 -187
- package/dist/SimpleMatchers/index.js +590 -288
- package/dist/SourceView/Highlighter.js +230 -70
- package/dist/SourceView/LineNo.js +72 -17
- package/dist/SourceView/index.js +177 -101
- package/dist/SourceView/lang-detector/ext-to-lang.json +798 -798
- package/dist/SourceView/lang-detector/index.js +28 -14
- package/dist/SourceView/useSelectedLineRange.js +97 -16
- package/dist/Table/ColorCell.js +42 -1
- package/dist/Table/ColumnsVisibility.js +114 -6
- package/dist/Table/MoreDropdown.js +121 -27
- package/dist/Table/TableContextMenu.js +150 -139
- package/dist/Table/TableContextMenuWrapper.js +59 -14
- package/dist/Table/hooks/useColorManagement.js +58 -16
- package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
- package/dist/Table/hooks/useTableConfiguration.js +331 -168
- package/dist/Table/index.js +222 -126
- package/dist/Table/utils/functions.js +169 -144
- package/dist/Table/utils/topAndBottomExpandedRowModel.js +69 -52
- package/dist/TimelineGuide/index.js +209 -16
- package/dist/TopTable/benchmarks/benchdata/populateData.js +91 -0
- package/dist/TopTable/index.js +340 -122
- package/dist/contexts/LabelsQueryProvider.js +94 -32
- package/dist/contexts/UnifiedLabelsContext.js +114 -49
- package/dist/contexts/utils.js +37 -15
- package/dist/hooks/useCompareModeMeta.js +157 -94
- package/dist/hooks/useLabels.js +295 -52
- package/dist/hooks/useQueryState.js +371 -330
- package/dist/index.js +21 -16
- package/dist/testdata/fg-diff.json +3750 -0
- package/dist/testdata/fg-simple.json +1879 -0
- package/dist/testdata/link_data.json +56 -0
- package/dist/testdata/tabular.json +30 -0
- package/dist/testdata/test_flamegraph.json +26846 -0
- package/dist/testdata/test_graph.json +53 -0
- package/dist/useDelayedLoader.js +32 -18
- package/dist/useGrpcQuery/index.js +71 -11
- package/dist/useHasProfileData.js +90 -12
- package/dist/useQuery.js +205 -64
- package/dist/useSumBy.d.ts.map +1 -1
- package/dist/useSumBy.js +294 -138
- package/dist/utils.js +62 -30
- package/package.json +9 -9
- package/src/GraphTooltipArrow/index.tsx +3 -0
- package/src/MatchersInput/SuggestionsList.test.tsx +70 -0
- package/src/MatchersInput/SuggestionsList.tsx +11 -10
- package/src/MatchersInput/index.tsx +1 -1
- package/src/MetricsGraph/MetricsTooltip/index.tsx +22 -34
- package/src/PreSelectedMatchers/index.tsx +3 -0
- package/src/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.tsx +3 -0
- package/src/ProfileFlameGraph/FlameGraphArrow/TooltipContext.tsx +3 -0
- package/src/ProfileFlameGraph/FlameGraphArrow/ZoomControls.tsx +3 -0
- package/src/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.ts +3 -0
- package/src/ProfileSelector/index.tsx +30 -7
- package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +3 -0
- package/src/ProfileView/components/VisualizationContainer/index.tsx +3 -0
- package/src/Table/hooks/useTableConfiguration.tsx +7 -13
- package/src/useDelayedLoader.ts +10 -10
- package/src/useSumBy.ts +12 -18
- package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.js +0 -541
- package/dist/hooks/useQueryState.test.js +0 -984
|
@@ -0,0 +1,70 @@
|
|
|
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 {useRef} from 'react';
|
|
15
|
+
|
|
16
|
+
import {act, fireEvent, render} from '@testing-library/react';
|
|
17
|
+
import {beforeAll, describe, expect, it, vi} from 'vitest';
|
|
18
|
+
|
|
19
|
+
import SuggestionsList, {Suggestion, Suggestions} from './SuggestionsList';
|
|
20
|
+
|
|
21
|
+
vi.mock('@parca/components', () => ({
|
|
22
|
+
RefreshButton: ({title}: {title: string}) => <button type="button">{title}</button>,
|
|
23
|
+
useParcaContext: () => ({
|
|
24
|
+
loader: <div>loading</div>,
|
|
25
|
+
}),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
beforeAll(() => {
|
|
29
|
+
Element.prototype.scrollIntoView = vi.fn();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const TestHarness = ({inputKey = 'initial'}: {inputKey?: string}): JSX.Element => {
|
|
33
|
+
const inputRef = useRef<HTMLTextAreaElement | null>(null);
|
|
34
|
+
const suggestions = new Suggestions();
|
|
35
|
+
suggestions.labelNames.push(new Suggestion('labelName', 'na', 'namespace'));
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div>
|
|
39
|
+
<textarea key={inputKey} ref={inputRef} />
|
|
40
|
+
<SuggestionsList
|
|
41
|
+
suggestions={suggestions}
|
|
42
|
+
applySuggestion={vi.fn()}
|
|
43
|
+
inputRef={inputRef}
|
|
44
|
+
runQuery={vi.fn()}
|
|
45
|
+
focusedInput
|
|
46
|
+
isLabelNamesLoading={false}
|
|
47
|
+
isLabelValuesLoading={false}
|
|
48
|
+
shouldTrimPrefix={false}
|
|
49
|
+
refetchLabelValues={vi.fn(async () => {})}
|
|
50
|
+
refetchLabelNames={vi.fn(async () => {})}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe('SuggestionsList', () => {
|
|
57
|
+
it('rebinds keyboard listeners when the textarea ref points to a remounted node', () => {
|
|
58
|
+
const {rerender, getByRole, getByText} = render(<TestHarness inputKey="first" />);
|
|
59
|
+
|
|
60
|
+
rerender(<TestHarness inputKey="second" />);
|
|
61
|
+
|
|
62
|
+
const textarea = getByRole('textbox');
|
|
63
|
+
act(() => {
|
|
64
|
+
fireEvent.keyDown(textarea, {key: 'ArrowDown'});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// eslint-disable-next-line jest-dom/prefer-to-have-class
|
|
68
|
+
expect(getByText('namespace').className).toContain('bg-indigo-600');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {Fragment, useCallback, useEffect, useState} from 'react';
|
|
14
|
+
import React, {Fragment, useCallback, useEffect, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {Transition} from '@headlessui/react';
|
|
17
17
|
import {usePopper} from 'react-popper';
|
|
@@ -48,7 +48,7 @@ export class Suggestions {
|
|
|
48
48
|
interface Props {
|
|
49
49
|
suggestions: Suggestions;
|
|
50
50
|
applySuggestion: (suggestion: Suggestion) => void;
|
|
51
|
-
inputRef: HTMLTextAreaElement | null
|
|
51
|
+
inputRef: React.RefObject<HTMLTextAreaElement | null>;
|
|
52
52
|
runQuery: () => void;
|
|
53
53
|
focusedInput: boolean;
|
|
54
54
|
isLabelNamesLoading: boolean;
|
|
@@ -82,7 +82,7 @@ const SuggestionsList = ({
|
|
|
82
82
|
refetchLabelNames,
|
|
83
83
|
}: Props): JSX.Element => {
|
|
84
84
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
85
|
-
const {styles, attributes} = usePopper(inputRef, popperElement, {
|
|
85
|
+
const {styles, attributes} = usePopper(inputRef.current, popperElement, {
|
|
86
86
|
placement: 'bottom-start',
|
|
87
87
|
});
|
|
88
88
|
const [highlightedSuggestionIndex, setHighlightedSuggestionIndex] = useState<number>(-1);
|
|
@@ -227,18 +227,19 @@ const SuggestionsList = ({
|
|
|
227
227
|
);
|
|
228
228
|
|
|
229
229
|
useEffect(() => {
|
|
230
|
-
|
|
230
|
+
const el = inputRef.current;
|
|
231
|
+
if (el == null) {
|
|
231
232
|
return;
|
|
232
233
|
}
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
|
|
235
|
+
el.addEventListener('keydown', handleKeyDown);
|
|
236
|
+
el.addEventListener('keypress', handleKeyPress as any);
|
|
236
237
|
|
|
237
238
|
return () => {
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
el.removeEventListener('keydown', handleKeyDown);
|
|
240
|
+
el.removeEventListener('keypress', handleKeyPress as any);
|
|
240
241
|
};
|
|
241
|
-
}
|
|
242
|
+
});
|
|
242
243
|
|
|
243
244
|
useEffect(() => {
|
|
244
245
|
if (suggestionsLength > 0 && focusedInput) {
|
|
@@ -263,7 +264,7 @@ const SuggestionsList = ({
|
|
|
263
264
|
leaveTo="opacity-0"
|
|
264
265
|
>
|
|
265
266
|
<div
|
|
266
|
-
style={{width: inputRef?.offsetWidth}}
|
|
267
|
+
style={{width: inputRef.current?.offsetWidth}}
|
|
267
268
|
className="absolute z-10 mt-1 max-h-[400px] rounded-md bg-gray-50 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none dark:bg-gray-900 sm:text-sm flex flex-col"
|
|
268
269
|
>
|
|
269
270
|
<div className="flex-1 overflow-auto min-h-0">
|
|
@@ -211,7 +211,7 @@ const MatchersInput = ({setDraftMatchers, draftParsedQuery, commitDraft}: Props)
|
|
|
211
211
|
isLabelNamesLoading={isLabelNamesLoading}
|
|
212
212
|
suggestions={suggestionSections}
|
|
213
213
|
applySuggestion={applySuggestion}
|
|
214
|
-
inputRef={inputRef
|
|
214
|
+
inputRef={inputRef}
|
|
215
215
|
runQuery={commitDraft}
|
|
216
216
|
focusedInput={focusedInput}
|
|
217
217
|
isLabelValuesLoading={
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/* eslint-disable react-hooks/refs */
|
|
15
|
+
|
|
16
|
+
import {useLayoutEffect, useRef, useState} from 'react';
|
|
15
17
|
|
|
16
18
|
import {usePopper} from 'react-popper';
|
|
17
19
|
|
|
@@ -28,21 +30,16 @@ interface Props {
|
|
|
28
30
|
content: React.ReactNode;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
y: 0,
|
|
42
|
-
toJSON: () => ({}),
|
|
43
|
-
};
|
|
44
|
-
return emptyRect;
|
|
45
|
-
},
|
|
33
|
+
const emptyRect: DOMRect = {
|
|
34
|
+
width: 0,
|
|
35
|
+
height: 0,
|
|
36
|
+
top: 0,
|
|
37
|
+
right: 0,
|
|
38
|
+
bottom: 0,
|
|
39
|
+
left: 0,
|
|
40
|
+
x: 0,
|
|
41
|
+
y: 0,
|
|
42
|
+
toJSON: () => ({}),
|
|
46
43
|
};
|
|
47
44
|
|
|
48
45
|
const createDomRect = (x: number, y: number): DOMRect => {
|
|
@@ -61,9 +58,13 @@ const createDomRect = (x: number, y: number): DOMRect => {
|
|
|
61
58
|
};
|
|
62
59
|
|
|
63
60
|
const MetricsTooltip = ({x, y, contextElement, content}: Props): JSX.Element => {
|
|
61
|
+
'use no memo';
|
|
64
62
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
|
63
|
+
const virtualElementRef = useRef<VirtualElement>({
|
|
64
|
+
getBoundingClientRect: () => emptyRect,
|
|
65
|
+
});
|
|
65
66
|
|
|
66
|
-
const {styles, attributes, update} = usePopper(
|
|
67
|
+
const {styles, attributes, update} = usePopper(virtualElementRef.current, popperElement, {
|
|
67
68
|
placement: 'auto-start',
|
|
68
69
|
strategy: 'absolute',
|
|
69
70
|
modifiers: [
|
|
@@ -82,26 +83,13 @@ const MetricsTooltip = ({x, y, contextElement, content}: Props): JSX.Element =>
|
|
|
82
83
|
],
|
|
83
84
|
});
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const domRect: DOMRect = (contextElement as Element)?.getBoundingClientRect() ??
|
|
88
|
-
width: 0,
|
|
89
|
-
height: 0,
|
|
90
|
-
top: 0,
|
|
91
|
-
right: 0,
|
|
92
|
-
bottom: 0,
|
|
93
|
-
left: 0,
|
|
94
|
-
x: 0,
|
|
95
|
-
y: 0,
|
|
96
|
-
toJSON: () => ({}),
|
|
97
|
-
};
|
|
86
|
+
useLayoutEffect(() => {
|
|
87
|
+
virtualElementRef.current.getBoundingClientRect = (): DOMRect => {
|
|
88
|
+
const domRect: DOMRect = (contextElement as Element)?.getBoundingClientRect() ?? emptyRect;
|
|
98
89
|
return createDomRect(domRect.x + x, domRect.y + y);
|
|
99
90
|
};
|
|
100
|
-
}, [x, y, contextElement]);
|
|
101
|
-
|
|
102
|
-
useEffect(() => {
|
|
103
91
|
void update?.();
|
|
104
|
-
}, [x, y, update]);
|
|
92
|
+
}, [x, y, contextElement, update]);
|
|
105
93
|
|
|
106
94
|
// Don't render anything if content is null or undefined
|
|
107
95
|
if (content == null) {
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/refs */
|
|
15
|
+
|
|
14
16
|
import React, {useCallback, useEffect, useRef, useState} from 'react';
|
|
15
17
|
|
|
16
18
|
import {Icon} from '@iconify/react';
|
|
@@ -29,6 +31,7 @@ interface Props {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
const PreSelectedMatchers: React.FC<Props> = ({labelNames}) => {
|
|
34
|
+
'use no memo';
|
|
32
35
|
const [labelValuesMap, setLabelValuesMap] = useState<Record<string, string[]>>({});
|
|
33
36
|
const [isLoading, setIsLoading] = useState<Record<string, boolean>>({});
|
|
34
37
|
const metadata = useGrpcMetadata();
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/set-state-in-effect */
|
|
15
|
+
|
|
14
16
|
import {useEffect, useRef, useState} from 'react';
|
|
15
17
|
|
|
16
18
|
import {useURLState} from '@parca/components';
|
|
@@ -64,6 +66,7 @@ function calculateTruncatedText(
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
function TextWithEllipsis({text, x, y, width}: Props): JSX.Element {
|
|
69
|
+
'use no memo';
|
|
67
70
|
const textRef = useRef<SVGTextElement>(null);
|
|
68
71
|
const [displayText, setDisplayText] = useState(text);
|
|
69
72
|
const [alignFunctionName] = useURLState('align_function_name');
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/refs */
|
|
15
|
+
|
|
14
16
|
import React, {createContext, useCallback, useContext, useMemo, useRef} from 'react';
|
|
15
17
|
|
|
16
18
|
import {Table} from '@uwdata/flechette';
|
|
@@ -68,6 +70,7 @@ export const TooltipProvider: React.FC<TooltipProviderProps> = ({
|
|
|
68
70
|
onTooltipUpdate,
|
|
69
71
|
tooltipId = 'default',
|
|
70
72
|
}) => {
|
|
73
|
+
'use no memo';
|
|
71
74
|
const tooltipStateRef = useRef<TooltipState>({row: null, x: 0, y: 0});
|
|
72
75
|
|
|
73
76
|
const updateTooltip = useCallback(
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/refs */
|
|
15
|
+
|
|
14
16
|
import React from 'react';
|
|
15
17
|
|
|
16
18
|
import {Icon} from '@iconify/react';
|
|
@@ -33,6 +35,7 @@ export const ZoomControls = ({
|
|
|
33
35
|
resetZoom,
|
|
34
36
|
portalRef,
|
|
35
37
|
}: ZoomControlsProps): React.JSX.Element => {
|
|
38
|
+
'use no memo';
|
|
36
39
|
const controls = (
|
|
37
40
|
<div className="flex items-center gap-1 rounded-md border border-gray-200 bg-white/90 px-1 py-0.5 shadow-sm backdrop-blur-sm dark:border-gray-600 dark:bg-gray-800/90">
|
|
38
41
|
<button
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/set-state-in-effect */
|
|
15
|
+
|
|
14
16
|
import {useEffect, useRef, useState} from 'react';
|
|
15
17
|
|
|
16
18
|
interface UseBatchedRenderingOptions {
|
|
@@ -29,6 +31,7 @@ export const useBatchedRendering = <T>(
|
|
|
29
31
|
items: T[],
|
|
30
32
|
options: UseBatchedRenderingOptions = {}
|
|
31
33
|
): UseBatchedRenderingResult<T> => {
|
|
34
|
+
'use no memo';
|
|
32
35
|
const {batchSize = 500, batchDelay = 0} = options;
|
|
33
36
|
|
|
34
37
|
const [renderedCount, setRenderedCount] = useState(0);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {Dispatch, SetStateAction, useCallback,
|
|
14
|
+
import {Dispatch, SetStateAction, useCallback, useMemo, useRef, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {RpcError} from '@protobuf-ts/runtime-rpc';
|
|
17
17
|
|
|
@@ -154,7 +154,20 @@ const ProfileSelector = ({
|
|
|
154
154
|
);
|
|
155
155
|
|
|
156
156
|
// Sync local timeRangeSelection when URL state changes externally (e.g., "Switch to 1 minute" button)
|
|
157
|
-
|
|
157
|
+
const [prevQueryTimeSelection, setPrevQueryTimeSelection] = useState(
|
|
158
|
+
querySelection.timeSelection
|
|
159
|
+
);
|
|
160
|
+
const [prevQueryFrom, setPrevQueryFrom] = useState(querySelection.from);
|
|
161
|
+
const [prevQueryTo, setPrevQueryTo] = useState(querySelection.to);
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
prevQueryTimeSelection !== querySelection.timeSelection ||
|
|
165
|
+
prevQueryFrom !== querySelection.from ||
|
|
166
|
+
prevQueryTo !== querySelection.to
|
|
167
|
+
) {
|
|
168
|
+
setPrevQueryTimeSelection(querySelection.timeSelection);
|
|
169
|
+
setPrevQueryFrom(querySelection.from);
|
|
170
|
+
setPrevQueryTo(querySelection.to);
|
|
158
171
|
setTimeRangeSelection(
|
|
159
172
|
DateTimeRange.fromRangeKey(
|
|
160
173
|
querySelection.timeSelection,
|
|
@@ -162,7 +175,7 @@ const ProfileSelector = ({
|
|
|
162
175
|
querySelection.to
|
|
163
176
|
)
|
|
164
177
|
);
|
|
165
|
-
}
|
|
178
|
+
}
|
|
166
179
|
|
|
167
180
|
const [queryExpressionString, setQueryExpressionString] = useState(draftSelection.expression);
|
|
168
181
|
|
|
@@ -198,18 +211,28 @@ const ProfileSelector = ({
|
|
|
198
211
|
return result.response?.labelNames === undefined ? [] : result.response.labelNames;
|
|
199
212
|
}, [result]);
|
|
200
213
|
|
|
201
|
-
|
|
214
|
+
const [prevEnforcedProfileName, setPrevEnforcedProfileName] = useState(enforcedProfileName);
|
|
215
|
+
const [prevQueryExpression, setPrevQueryExpression] = useState(querySelection.expression);
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
prevEnforcedProfileName !== enforcedProfileName ||
|
|
219
|
+
prevQueryExpression !== querySelection.expression
|
|
220
|
+
) {
|
|
221
|
+
setPrevEnforcedProfileName(enforcedProfileName);
|
|
222
|
+
setPrevQueryExpression(querySelection.expression);
|
|
202
223
|
if (enforcedProfileName !== '') {
|
|
203
224
|
const [q, changed] = Query.parse(querySelection.expression).setProfileName(
|
|
204
225
|
enforcedProfileName
|
|
205
226
|
);
|
|
206
227
|
if (changed) {
|
|
207
228
|
setQueryExpressionString(q.toString());
|
|
208
|
-
|
|
229
|
+
} else {
|
|
230
|
+
setQueryExpressionString(querySelection.expression);
|
|
209
231
|
}
|
|
232
|
+
} else {
|
|
233
|
+
setQueryExpressionString(querySelection.expression);
|
|
210
234
|
}
|
|
211
|
-
|
|
212
|
-
}, [enforcedProfileName, querySelection.expression]);
|
|
235
|
+
}
|
|
213
236
|
|
|
214
237
|
const enforcedProfileNameQuery = (): Query => {
|
|
215
238
|
const pq = Query.parse(queryExpressionString);
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/set-state-in-effect */
|
|
15
|
+
|
|
14
16
|
import React, {useCallback, useEffect, useRef, useState} from 'react';
|
|
15
17
|
|
|
16
18
|
import {Menu} from '@headlessui/react';
|
|
@@ -73,6 +75,7 @@ const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
73
75
|
customSubmenu,
|
|
74
76
|
renderAsDiv = false,
|
|
75
77
|
}) => {
|
|
78
|
+
'use no memo';
|
|
76
79
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
77
80
|
const [shouldOpenLeft, setShouldOpenLeft] = useState(false);
|
|
78
81
|
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
+
/* eslint-disable react-hooks/refs */
|
|
15
|
+
|
|
14
16
|
import {FC} from 'react';
|
|
15
17
|
|
|
16
18
|
import cx from 'classnames';
|
|
@@ -42,6 +44,7 @@ export const VisualizationContainer: FC<VisualizationContainerProps> = ({
|
|
|
42
44
|
index,
|
|
43
45
|
actionButtons,
|
|
44
46
|
}) => {
|
|
47
|
+
'use no memo';
|
|
45
48
|
const {handleClosePanel} = useDashboard();
|
|
46
49
|
|
|
47
50
|
return (
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {useMemo} from 'react';
|
|
15
15
|
|
|
16
16
|
import {createColumnHelper, type ColumnDef} from '@tanstack/table-core';
|
|
17
17
|
|
|
@@ -47,8 +47,8 @@ export function useTableConfiguration({
|
|
|
47
47
|
alwaysReturnArray: true,
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
const
|
|
51
|
-
|
|
50
|
+
const columnVisibility = useMemo(() => {
|
|
51
|
+
const defaults: Record<string, boolean> = {
|
|
52
52
|
color: true,
|
|
53
53
|
flat: true,
|
|
54
54
|
flatPercentage: false,
|
|
@@ -63,19 +63,13 @@ export function useTableConfiguration({
|
|
|
63
63
|
functionFileName: false,
|
|
64
64
|
mappingFile: false,
|
|
65
65
|
};
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
useEffect(() => {
|
|
69
66
|
if (Array.isArray(tableColumns)) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
(Object.keys(newState) as ColumnName[]).forEach(column => {
|
|
73
|
-
newState[column] = tableColumns.includes(column);
|
|
74
|
-
});
|
|
75
|
-
return newState;
|
|
67
|
+
(Object.keys(defaults) as ColumnName[]).forEach(column => {
|
|
68
|
+
defaults[column] = tableColumns.includes(column);
|
|
76
69
|
});
|
|
77
70
|
}
|
|
78
|
-
|
|
71
|
+
return defaults;
|
|
72
|
+
}, [tableColumns, compareMode]);
|
|
79
73
|
|
|
80
74
|
const columns = useMemo<Array<ColumnDef<Row>>>(() => {
|
|
81
75
|
const baseColumns: Array<ColumnDef<Row>> = [
|
package/src/useDelayedLoader.ts
CHANGED
|
@@ -18,20 +18,20 @@ interface DelayedLoaderOptions {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const useDelayedLoader = (isLoading = false, options?: DelayedLoaderOptions): boolean => {
|
|
21
|
+
'use no memo';
|
|
21
22
|
const {delay = 500} = options ?? {};
|
|
22
23
|
const [isLoaderVisible, setIsLoaderVisible] = useState<boolean>(false);
|
|
23
24
|
useEffect(() => {
|
|
24
|
-
|
|
25
|
-
if
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
if (!isLoading) return;
|
|
26
|
+
// if the request takes longer than half a second, show the loading icon
|
|
27
|
+
const showLoaderTimeout = setTimeout(() => {
|
|
28
|
+
setIsLoaderVisible(true);
|
|
29
|
+
}, delay);
|
|
30
|
+
return () => {
|
|
31
|
+
clearTimeout(showLoaderTimeout);
|
|
31
32
|
setIsLoaderVisible(false);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
}, [isLoading, isLoaderVisible, delay]);
|
|
33
|
+
};
|
|
34
|
+
}, [isLoading, delay]);
|
|
35
35
|
|
|
36
36
|
return isLoaderVisible;
|
|
37
37
|
};
|
package/src/useSumBy.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// See the License for the specific language governing permissions and
|
|
12
12
|
// limitations under the License.
|
|
13
13
|
|
|
14
|
-
import {useCallback,
|
|
14
|
+
import {useCallback, useMemo, useState} from 'react';
|
|
15
15
|
|
|
16
16
|
import {QueryServiceClient} from '@parca/client';
|
|
17
17
|
import {DateTimeRange} from '@parca/components';
|
|
@@ -70,14 +70,19 @@ export const useSumBySelection = (
|
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
// Update userSelectedSumBy when defaultValue changes (e.g., during navigation)
|
|
73
|
-
|
|
73
|
+
const [prevProfileType, setPrevProfileType] = useState(profileType);
|
|
74
|
+
const [prevDefaultValue, setPrevDefaultValue] = useState(defaultValue);
|
|
75
|
+
|
|
76
|
+
if (prevProfileType !== profileType || prevDefaultValue !== defaultValue) {
|
|
77
|
+
setPrevProfileType(profileType);
|
|
78
|
+
setPrevDefaultValue(defaultValue);
|
|
74
79
|
if (profileType != null && defaultValue !== undefined) {
|
|
75
80
|
setUserSelectedSumBy(prev => ({
|
|
76
81
|
...prev,
|
|
77
82
|
[profileType.toString()]: defaultValue,
|
|
78
83
|
}));
|
|
79
84
|
}
|
|
80
|
-
}
|
|
85
|
+
}
|
|
81
86
|
|
|
82
87
|
const setSumBy = useCallback(
|
|
83
88
|
(sumBy: string[]) => {
|
|
@@ -97,19 +102,11 @@ export const useSumBySelection = (
|
|
|
97
102
|
|
|
98
103
|
const {defaultSumBy} = useDefaultSumBy(profileType, labelNamesLoading, labels);
|
|
99
104
|
|
|
100
|
-
// Store the last valid sumBy value to return during loading
|
|
101
|
-
const lastValidSumByRef = useRef<string[]>(DEFAULT_EMPTY_SUM_BY);
|
|
102
|
-
|
|
103
105
|
const sumBy = useMemo(() => {
|
|
104
|
-
if
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return draftSumBy;
|
|
109
|
-
}
|
|
110
|
-
if (lastValidSumByRef.current == null) {
|
|
111
|
-
return lastValidSumByRef.current;
|
|
112
|
-
}
|
|
106
|
+
// For smoother UX, return draftSumBy first if available during loading
|
|
107
|
+
// as this must be recently computed with the draft time range labels.
|
|
108
|
+
if (labelNamesLoading && draftSumBy !== undefined) {
|
|
109
|
+
return draftSumBy;
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
// Prefer non-empty URL default over auto-computed default to avoid a
|
|
@@ -125,9 +122,6 @@ export const useSumBySelection = (
|
|
|
125
122
|
result = DEFAULT_EMPTY_SUM_BY;
|
|
126
123
|
}
|
|
127
124
|
|
|
128
|
-
// Store the computed value for next loading state
|
|
129
|
-
lastValidSumByRef.current = result;
|
|
130
|
-
|
|
131
125
|
return result;
|
|
132
126
|
}, [userSelectedSumBy, profileType, defaultSumBy, labelNamesLoading, draftSumBy, defaultValue]);
|
|
133
127
|
|