@parca/profile 0.19.140 → 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.
Files changed (253) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/GraphTooltipArrow/Content.js +224 -30
  3. package/dist/GraphTooltipArrow/DockedGraphTooltip/index.js +192 -33
  4. package/dist/GraphTooltipArrow/ExpandOnHoverValue.js +53 -3
  5. package/dist/GraphTooltipArrow/index.d.ts.map +1 -1
  6. package/dist/GraphTooltipArrow/index.js +86 -56
  7. package/dist/GraphTooltipArrow/useGraphTooltip/index.js +37 -37
  8. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.d.ts.map +1 -1
  9. package/dist/GraphTooltipArrow/useGraphTooltipMetaInfo/index.js +104 -72
  10. package/dist/MatchersInput/SuggestionItem.js +91 -12
  11. package/dist/MatchersInput/SuggestionsList.d.ts +2 -1
  12. package/dist/MatchersInput/SuggestionsList.d.ts.map +1 -1
  13. package/dist/MatchersInput/SuggestionsList.js +371 -157
  14. package/dist/MatchersInput/SuggestionsList.test.d.ts +2 -0
  15. package/dist/MatchersInput/SuggestionsList.test.d.ts.map +1 -0
  16. package/dist/MatchersInput/index.js +308 -115
  17. package/dist/MetricsCircle/index.js +39 -3
  18. package/dist/MetricsGraph/MetricsContextMenu/index.js +119 -19
  19. package/dist/MetricsGraph/MetricsInfoPanel/index.js +81 -20
  20. package/dist/MetricsGraph/MetricsTooltip/index.d.ts.map +1 -1
  21. package/dist/MetricsGraph/MetricsTooltip/index.js +107 -74
  22. package/dist/MetricsGraph/index.js +552 -203
  23. package/dist/MetricsGraph/useMetricsGraphDimensions.js +46 -25
  24. package/dist/MetricsGraph/utils/colorMapping.js +24 -17
  25. package/dist/MetricsSeries/index.js +70 -7
  26. package/dist/PreSelectedMatchers/index.d.ts.map +1 -1
  27. package/dist/PreSelectedMatchers/index.js +249 -102
  28. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
  29. package/dist/ProfileExplorer/ProfileExplorerCompare.js +241 -45
  30. package/dist/ProfileExplorer/ProfileExplorerSingle.js +98 -11
  31. package/dist/ProfileExplorer/index.js +183 -32
  32. package/dist/ProfileFlameChart/SamplesStrips/SamplesGraph/index.js +333 -148
  33. package/dist/ProfileFlameChart/SamplesStrips/SamplesStrips.stories.js +69 -35
  34. package/dist/ProfileFlameChart/SamplesStrips/index.d.ts +2 -2
  35. package/dist/ProfileFlameChart/SamplesStrips/index.d.ts.map +1 -1
  36. package/dist/ProfileFlameChart/SamplesStrips/index.js +645 -134
  37. package/dist/ProfileFlameChart/SamplesStrips/labelSetUtils.js +114 -55
  38. package/dist/ProfileFlameChart/index.d.ts.map +1 -1
  39. package/dist/ProfileFlameChart/index.js +267 -129
  40. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.d.ts.map +1 -1
  41. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenu.js +288 -89
  42. package/dist/ProfileFlameGraph/FlameGraphArrow/ContextMenuWrapper.js +56 -20
  43. package/dist/ProfileFlameGraph/FlameGraphArrow/FlameGraphNodes.js +211 -140
  44. package/dist/ProfileFlameGraph/FlameGraphArrow/MemoizedTooltip.js +133 -38
  45. package/dist/ProfileFlameGraph/FlameGraphArrow/MiniMap.js +261 -216
  46. package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.d.ts.map +1 -1
  47. package/dist/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.js +72 -47
  48. package/dist/ProfileFlameGraph/FlameGraphArrow/TooltipContext.d.ts.map +1 -1
  49. package/dist/ProfileFlameGraph/FlameGraphArrow/TooltipContext.js +58 -28
  50. package/dist/ProfileFlameGraph/FlameGraphArrow/ZoomControls.d.ts.map +1 -1
  51. package/dist/ProfileFlameGraph/FlameGraphArrow/ZoomControls.js +59 -8
  52. package/dist/ProfileFlameGraph/FlameGraphArrow/index.js +396 -179
  53. package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.d.ts.map +1 -1
  54. package/dist/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.js +68 -50
  55. package/dist/ProfileFlameGraph/FlameGraphArrow/useMappingList.js +62 -38
  56. package/dist/ProfileFlameGraph/FlameGraphArrow/useNodeColor.js +14 -6
  57. package/dist/ProfileFlameGraph/FlameGraphArrow/useScrollViewport.js +124 -82
  58. package/dist/ProfileFlameGraph/FlameGraphArrow/useVisibleNodes.js +160 -98
  59. package/dist/ProfileFlameGraph/FlameGraphArrow/useZoom.js +232 -112
  60. package/dist/ProfileFlameGraph/FlameGraphArrow/utils.js +137 -114
  61. package/dist/ProfileFlameGraph/benchmarks/benchdata/populateData.js +85 -0
  62. package/dist/ProfileFlameGraph/index.d.ts.map +1 -1
  63. package/dist/ProfileFlameGraph/index.js +324 -150
  64. package/dist/ProfileMetricsGraph/hooks/useQueryRange.js +140 -32
  65. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  66. package/dist/ProfileMetricsGraph/index.js +519 -258
  67. package/dist/ProfileSelector/CompareButton.js +132 -12
  68. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  69. package/dist/ProfileSelector/MetricsGraphSection.js +236 -64
  70. package/dist/ProfileSelector/index.d.ts.map +1 -1
  71. package/dist/ProfileSelector/index.js +727 -141
  72. package/dist/ProfileSelector/useAutoQuerySelector.js +249 -130
  73. package/dist/ProfileSource.js +230 -163
  74. package/dist/ProfileTypeSelector/index.js +214 -125
  75. package/dist/ProfileView/components/ActionButtons/GroupByDropdown.js +50 -4
  76. package/dist/ProfileView/components/ActionButtons/SortByDropdown.d.ts.map +1 -1
  77. package/dist/ProfileView/components/ActionButtons/SortByDropdown.js +141 -35
  78. package/dist/ProfileView/components/ColorStackLegend.d.ts.map +1 -1
  79. package/dist/ProfileView/components/ColorStackLegend.js +185 -55
  80. package/dist/ProfileView/components/DashboardItems/index.js +87 -28
  81. package/dist/ProfileView/components/DashboardLayout/index.js +108 -16
  82. package/dist/ProfileView/components/DiffLegend.js +172 -29
  83. package/dist/ProfileView/components/GroupByLabelsDropdown/index.js +199 -55
  84. package/dist/ProfileView/components/InvertCallStack/index.d.ts.map +1 -1
  85. package/dist/ProfileView/components/InvertCallStack/index.js +100 -12
  86. package/dist/ProfileView/components/ProfileFilters/filterPresets.js +260 -315
  87. package/dist/ProfileView/components/ProfileFilters/index.js +518 -215
  88. package/dist/ProfileView/components/ProfileFilters/useProfileFilters.js +370 -306
  89. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts +2 -1
  90. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.d.ts.map +1 -1
  91. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.js +188 -118
  92. package/dist/ProfileView/components/ProfileHeader/index.js +105 -11
  93. package/dist/ProfileView/components/ShareButton/ResultBox.js +119 -16
  94. package/dist/ProfileView/components/ShareButton/index.js +352 -62
  95. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.d.ts.map +1 -1
  96. package/dist/ProfileView/components/Toolbars/MultiLevelDropdown.js +678 -194
  97. package/dist/ProfileView/components/Toolbars/SwitchMenuItem.js +94 -7
  98. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.d.ts.map +1 -1
  99. package/dist/ProfileView/components/Toolbars/TableColumnsDropdown.js +199 -157
  100. package/dist/ProfileView/components/Toolbars/index.d.ts +2 -2
  101. package/dist/ProfileView/components/Toolbars/index.d.ts.map +1 -1
  102. package/dist/ProfileView/components/Toolbars/index.js +441 -21
  103. package/dist/ProfileView/components/ViewSelector/Dropdown.js +233 -22
  104. package/dist/ProfileView/components/ViewSelector/index.d.ts.map +1 -1
  105. package/dist/ProfileView/components/ViewSelector/index.js +212 -86
  106. package/dist/ProfileView/components/VisualizationContainer/index.d.ts.map +1 -1
  107. package/dist/ProfileView/components/VisualizationContainer/index.js +52 -7
  108. package/dist/ProfileView/components/VisualizationPanel.js +185 -8
  109. package/dist/ProfileView/context/DashboardContext.d.ts.map +1 -1
  110. package/dist/ProfileView/context/DashboardContext.js +85 -29
  111. package/dist/ProfileView/context/ProfileViewContext.js +56 -15
  112. package/dist/ProfileView/hooks/useAutoSelectDimension.js +71 -41
  113. package/dist/ProfileView/hooks/useProfileMetadata.js +50 -18
  114. package/dist/ProfileView/hooks/useResetFlameGraphState.d.ts.map +1 -1
  115. package/dist/ProfileView/hooks/useResetFlameGraphState.js +32 -12
  116. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.d.ts.map +1 -1
  117. package/dist/ProfileView/hooks/useResetStateOnProfileTypeChange.js +71 -27
  118. package/dist/ProfileView/hooks/useResetStateOnSeriesChange.d.ts.map +1 -1
  119. package/dist/ProfileView/hooks/useResetStateOnSeriesChange.js +40 -19
  120. package/dist/ProfileView/hooks/useVisualizationState.d.ts +3 -3
  121. package/dist/ProfileView/hooks/useVisualizationState.d.ts.map +1 -1
  122. package/dist/ProfileView/hooks/useVisualizationState.js +258 -67
  123. package/dist/ProfileView/index.js +383 -45
  124. package/dist/ProfileView/types/visualization.js +1 -13
  125. package/dist/ProfileView/utils/colorUtils.js +8 -7
  126. package/dist/ProfileViewWithData.d.ts.map +1 -1
  127. package/dist/ProfileViewWithData.js +332 -228
  128. package/dist/QueryControls/index.js +418 -47
  129. package/dist/Sandwich/components/CalleesSection.js +54 -4
  130. package/dist/Sandwich/components/CallersSection.js +97 -27
  131. package/dist/Sandwich/components/TableSection.js +77 -4
  132. package/dist/Sandwich/index.d.ts.map +1 -1
  133. package/dist/Sandwich/index.js +126 -14
  134. package/dist/Sandwich/utils/processRowData.js +48 -39
  135. package/dist/SelectWithRefresh/index.js +102 -28
  136. package/dist/SimpleMatchers/Select.js +520 -187
  137. package/dist/SimpleMatchers/index.js +590 -288
  138. package/dist/SourceView/Highlighter.js +230 -70
  139. package/dist/SourceView/LineNo.js +72 -17
  140. package/dist/SourceView/index.d.ts.map +1 -1
  141. package/dist/SourceView/index.js +178 -104
  142. package/dist/SourceView/lang-detector/ext-to-lang.json +798 -798
  143. package/dist/SourceView/lang-detector/index.js +28 -14
  144. package/dist/SourceView/useSelectedLineRange.d.ts.map +1 -1
  145. package/dist/SourceView/useSelectedLineRange.js +99 -23
  146. package/dist/Table/ColorCell.js +42 -1
  147. package/dist/Table/ColumnsVisibility.js +114 -6
  148. package/dist/Table/MoreDropdown.d.ts.map +1 -1
  149. package/dist/Table/MoreDropdown.js +122 -25
  150. package/dist/Table/TableContextMenu.d.ts.map +1 -1
  151. package/dist/Table/TableContextMenu.js +151 -137
  152. package/dist/Table/TableContextMenuWrapper.js +59 -14
  153. package/dist/Table/hooks/useColorManagement.js +58 -16
  154. package/dist/Table/hooks/useTableConfiguration.d.ts.map +1 -1
  155. package/dist/Table/hooks/useTableConfiguration.js +333 -169
  156. package/dist/Table/index.d.ts.map +1 -1
  157. package/dist/Table/index.js +222 -128
  158. package/dist/Table/utils/functions.js +169 -144
  159. package/dist/Table/utils/topAndBottomExpandedRowModel.js +69 -52
  160. package/dist/TimelineGuide/index.js +209 -16
  161. package/dist/TopTable/benchmarks/benchdata/populateData.js +91 -0
  162. package/dist/TopTable/index.d.ts.map +1 -1
  163. package/dist/TopTable/index.js +342 -123
  164. package/dist/contexts/LabelsQueryProvider.js +94 -32
  165. package/dist/contexts/UnifiedLabelsContext.js +114 -49
  166. package/dist/contexts/utils.js +37 -15
  167. package/dist/hooks/useCompareModeMeta.d.ts.map +1 -1
  168. package/dist/hooks/useCompareModeMeta.js +158 -64
  169. package/dist/hooks/useLabels.js +295 -52
  170. package/dist/hooks/useQueryState.d.ts +3 -3
  171. package/dist/hooks/useQueryState.d.ts.map +1 -1
  172. package/dist/hooks/useQueryState.js +373 -332
  173. package/dist/index.d.ts +2 -3
  174. package/dist/index.d.ts.map +1 -1
  175. package/dist/index.js +22 -8
  176. package/dist/testdata/fg-diff.json +3750 -0
  177. package/dist/testdata/fg-simple.json +1879 -0
  178. package/dist/testdata/link_data.json +56 -0
  179. package/dist/testdata/tabular.json +30 -0
  180. package/dist/testdata/test_flamegraph.json +26846 -0
  181. package/dist/testdata/test_graph.json +53 -0
  182. package/dist/useDelayedLoader.js +32 -18
  183. package/dist/useGrpcQuery/index.js +71 -11
  184. package/dist/useHasProfileData.js +90 -12
  185. package/dist/useQuery.js +205 -64
  186. package/dist/useSumBy.d.ts +1 -1
  187. package/dist/useSumBy.d.ts.map +1 -1
  188. package/dist/useSumBy.js +294 -138
  189. package/dist/utils.js +62 -30
  190. package/package.json +9 -10
  191. package/src/GraphTooltipArrow/index.tsx +3 -0
  192. package/src/GraphTooltipArrow/useGraphTooltipMetaInfo/index.ts +13 -11
  193. package/src/MatchersInput/SuggestionsList.test.tsx +70 -0
  194. package/src/MatchersInput/SuggestionsList.tsx +11 -10
  195. package/src/MatchersInput/index.tsx +1 -1
  196. package/src/MetricsGraph/MetricsTooltip/index.tsx +22 -34
  197. package/src/PreSelectedMatchers/index.tsx +3 -0
  198. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +9 -4
  199. package/src/ProfileFlameChart/SamplesStrips/index.tsx +2 -2
  200. package/src/ProfileFlameChart/index.tsx +28 -21
  201. package/src/ProfileFlameGraph/FlameGraphArrow/ContextMenu.tsx +9 -10
  202. package/src/ProfileFlameGraph/FlameGraphArrow/TextWithEllipsis.tsx +6 -5
  203. package/src/ProfileFlameGraph/FlameGraphArrow/TooltipContext.tsx +3 -0
  204. package/src/ProfileFlameGraph/FlameGraphArrow/ZoomControls.tsx +3 -0
  205. package/src/ProfileFlameGraph/FlameGraphArrow/useBatchedRendering.ts +3 -0
  206. package/src/ProfileFlameGraph/index.tsx +9 -6
  207. package/src/ProfileMetricsGraph/index.tsx +8 -6
  208. package/src/ProfileSelector/MetricsGraphSection.tsx +10 -5
  209. package/src/ProfileSelector/index.tsx +61 -39
  210. package/src/ProfileView/components/ActionButtons/SortByDropdown.tsx +6 -10
  211. package/src/ProfileView/components/ColorStackLegend.tsx +4 -2
  212. package/src/ProfileView/components/InvertCallStack/index.tsx +4 -5
  213. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.tsx +192 -94
  214. package/src/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.ts +21 -21
  215. package/src/ProfileView/components/Toolbars/MultiLevelDropdown.tsx +28 -24
  216. package/src/ProfileView/components/Toolbars/TableColumnsDropdown.tsx +5 -4
  217. package/src/ProfileView/components/Toolbars/index.tsx +3 -3
  218. package/src/ProfileView/components/ViewSelector/index.tsx +16 -9
  219. package/src/ProfileView/components/VisualizationContainer/index.tsx +3 -0
  220. package/src/ProfileView/context/DashboardContext.tsx +6 -6
  221. package/src/ProfileView/hooks/useResetFlameGraphState.ts +4 -6
  222. package/src/ProfileView/hooks/useResetStateOnProfileTypeChange.ts +26 -24
  223. package/src/ProfileView/hooks/useResetStateOnSeriesChange.ts +8 -16
  224. package/src/ProfileView/hooks/useVisualizationState.ts +69 -61
  225. package/src/ProfileViewWithData.tsx +35 -29
  226. package/src/Sandwich/index.tsx +3 -4
  227. package/src/SourceView/index.tsx +2 -4
  228. package/src/SourceView/useSelectedLineRange.ts +19 -34
  229. package/src/Table/MoreDropdown.tsx +11 -9
  230. package/src/Table/TableContextMenu.tsx +13 -10
  231. package/src/Table/hooks/useTableConfiguration.tsx +11 -16
  232. package/src/Table/index.tsx +21 -12
  233. package/src/TopTable/index.tsx +4 -3
  234. package/src/hooks/useCompareModeMeta.ts +91 -61
  235. package/src/hooks/useQueryState.test.tsx +345 -275
  236. package/src/hooks/useQueryState.ts +118 -136
  237. package/src/index.tsx +15 -16
  238. package/src/useDelayedLoader.ts +10 -10
  239. package/src/useSumBy.ts +15 -21
  240. package/dist/ProfileView/components/ProfileFilters/useProfileFiltersUrlState.test.js +0 -455
  241. package/dist/hooks/urlParsers.d.ts +0 -18
  242. package/dist/hooks/urlParsers.d.ts.map +0 -1
  243. package/dist/hooks/urlParsers.js +0 -32
  244. package/dist/hooks/useColorBy.d.ts +0 -5
  245. package/dist/hooks/useColorBy.d.ts.map +0 -1
  246. package/dist/hooks/useColorBy.js +0 -26
  247. package/dist/hooks/useDashboardItems.d.ts +0 -5
  248. package/dist/hooks/useDashboardItems.d.ts.map +0 -1
  249. package/dist/hooks/useDashboardItems.js +0 -27
  250. package/dist/hooks/useQueryState.test.js +0 -868
  251. package/src/hooks/urlParsers.ts +0 -38
  252. package/src/hooks/useColorBy.ts +0 -42
  253. package/src/hooks/useDashboardItems.ts +0 -46
@@ -12,10 +12,9 @@
12
12
  // limitations under the License.
13
13
 
14
14
  import {Table} from '@uwdata/flechette';
15
- import {useQueryState} from 'nuqs';
16
15
 
17
16
  import {QueryRequest_ReportType} from '@parca/client';
18
- import {useParcaContext} from '@parca/components';
17
+ import {useParcaContext, useURLState} from '@parca/components';
19
18
 
20
19
  import {
21
20
  FIELD_FUNCTION_FILE_NAME,
@@ -31,8 +30,6 @@ import {
31
30
  import {arrowToString} from '../../ProfileFlameGraph/FlameGraphArrow/utils';
32
31
  import {ProfileSource} from '../../ProfileSource';
33
32
  import {useProfileViewContext} from '../../ProfileView/context/ProfileViewContext';
34
- import {stringParam} from '../../hooks/urlParsers';
35
- import {useDashboardItems} from '../../hooks/useDashboardItems';
36
33
  import {useQuery} from '../../useQuery';
37
34
 
38
35
  interface Props {
@@ -110,23 +107,28 @@ export const useGraphTooltipMetaInfo = ({table, row}: Props): GraphTooltipMetaIn
110
107
  ])
111
108
  .filter(value => value[1] !== '') as Array<[string, string]>;
112
109
 
113
- const {dashboardItems, setDashboardItems} = useDashboardItems();
110
+ const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
111
+ alwaysReturnArray: true,
112
+ });
114
113
 
115
- const [_unusedBuildId, setSourceBuildId] = useQueryState('source_buildid', stringParam);
114
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
115
+ const [unusedBuildId, setSourceBuildId] = useURLState('source_buildid');
116
116
 
117
- const [_unusedFilename, setSourceFilename] = useQueryState('source_filename', stringParam);
117
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
118
+ const [unusedFilename, setSourceFilename] = useURLState('source_filename');
118
119
 
119
- const [_unusedLine, setSourceLine] = useQueryState('source_line', stringParam);
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ const [unusedLine, setSourceLine] = useURLState('source_line');
120
122
 
121
123
  const openFile = (): void => {
122
124
  setDashboardItems([dashboardItems[0], 'source']);
123
125
  if (mappingBuildID != null) {
124
- void setSourceBuildId(mappingBuildID);
126
+ setSourceBuildId(mappingBuildID);
125
127
  }
126
128
 
127
- void setSourceFilename(functionFilename);
129
+ setSourceFilename(functionFilename);
128
130
  if (lineNumber !== undefined) {
129
- void setSourceLine(lineNumber.toString());
131
+ setSourceLine(lineNumber.toString());
130
132
  }
131
133
  };
132
134
 
@@ -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
- if (inputRef == null) {
230
+ const el = inputRef.current;
231
+ if (el == null) {
231
232
  return;
232
233
  }
233
234
 
234
- inputRef.addEventListener('keydown', handleKeyDown);
235
- inputRef.addEventListener('keypress', handleKeyPress as any);
235
+ el.addEventListener('keydown', handleKeyDown);
236
+ el.addEventListener('keypress', handleKeyPress as any);
236
237
 
237
238
  return () => {
238
- inputRef.removeEventListener('keydown', handleKeyDown);
239
- inputRef.removeEventListener('keypress', handleKeyPress as any);
239
+ el.removeEventListener('keydown', handleKeyDown);
240
+ el.removeEventListener('keypress', handleKeyPress as any);
240
241
  };
241
- }, [inputRef, highlightedSuggestionIndex, suggestions, handleKeyPress, handleKeyDown]);
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.current}
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
- import {useEffect, useMemo, useState} from 'react';
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 virtualElement: VirtualElement = {
32
- getBoundingClientRect: () => {
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: () => ({}),
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(virtualElement, popperElement, {
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
- useMemo(() => {
86
- virtualElement.getBoundingClientRect = (): DOMRect => {
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();
@@ -14,6 +14,7 @@
14
14
  import {useCallback, useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {QueryServiceClient} from '@parca/client';
17
+ import {useURLStateBatch} from '@parca/components';
17
18
  import {Query} from '@parca/parser';
18
19
  import {TEST_IDS, testId} from '@parca/test-utils';
19
20
  import type {NavigateFunction} from '@parca/utilities';
@@ -33,6 +34,7 @@ const ProfileExplorerCompare = ({
33
34
  navigateTo,
34
35
  }: ProfileExplorerCompareProps): JSX.Element => {
35
36
  const [showMetricsGraph, setShowMetricsGraph] = useState(true);
37
+ const batchUpdates = useURLStateBatch();
36
38
  const {closeCompareMode, isCompareMode, isCompareAbsolute} = useCompareModeMeta();
37
39
 
38
40
  // Read ProfileSource states from URL for both sides
@@ -63,10 +65,12 @@ const ProfileExplorerCompare = ({
63
65
  }
64
66
 
65
67
  if (querySelectionB.expression === '' && querySelectionA.expression !== '') {
66
- setDraftExpressionB(querySelectionA.expression);
67
- setDraftTimeRangeB(querySelectionA.from, querySelectionA.to, querySelectionA.timeSelection);
68
- // Commit to update the URL and trigger metrics graph load
69
- commitDraftB();
68
+ batchUpdates(() => {
69
+ setDraftExpressionB(querySelectionA.expression);
70
+ setDraftTimeRangeB(querySelectionA.from, querySelectionA.to, querySelectionA.timeSelection);
71
+ // Commit to update the URL and trigger metrics graph load
72
+ commitDraftB();
73
+ });
70
74
  }
71
75
  }, [
72
76
  isCompareMode,
@@ -78,6 +82,7 @@ const ProfileExplorerCompare = ({
78
82
  setDraftExpressionB,
79
83
  setDraftTimeRangeB,
80
84
  commitDraftB,
85
+ batchUpdates,
81
86
  ]);
82
87
 
83
88
  const closeProfileA = useCallback((): void => {
@@ -39,10 +39,10 @@ interface Props {
39
39
  loading?: boolean;
40
40
  cpus: LabelSet[];
41
41
  data: DataPoint[][];
42
- selectedTimeframe: {
42
+ selectedTimeframe?: {
43
43
  labels: LabelSet;
44
44
  bounds: NumberDuo;
45
- } | null;
45
+ };
46
46
  onSelectedTimeframe: (labels: LabelSet, bounds: NumberDuo | undefined) => void;
47
47
  width?: number;
48
48
  bounds: NumberDuo;
@@ -13,10 +13,14 @@
13
13
 
14
14
  import {useEffect, useMemo, useRef} from 'react';
15
15
 
16
- import {createParser, useQueryState} from 'nuqs';
17
-
18
16
  import {LabelSet, QueryRequest_ReportType, QueryServiceClient} from '@parca/client';
19
- import {Button, useParcaContext} from '@parca/components';
17
+ import {
18
+ Button,
19
+ useParcaContext,
20
+ useURLState,
21
+ useURLStateCustom,
22
+ type OptionsCustom,
23
+ } from '@parca/components';
20
24
  import {Matcher, MatcherTypes, ProfileType, Query} from '@parca/parser';
21
25
  import {TimeUnits, formatDate, formatDuration} from '@parca/utilities';
22
26
 
@@ -25,7 +29,6 @@ import {boundsFromProfileSource} from '../ProfileFlameGraph/FlameGraphArrow/util
25
29
  import {MergedProfileSource, ProfileSource, timeFormat} from '../ProfileSource';
26
30
  import {useProfileFilters} from '../ProfileView/components/ProfileFilters/useProfileFilters';
27
31
  import type {SamplesData} from '../ProfileView/types/visualization';
28
- import {flamechartDimensionParser} from '../hooks/urlParsers';
29
32
  import {useQuery} from '../useQuery';
30
33
  import {NumberDuo} from '../utils';
31
34
  import {SamplesStrip} from './SamplesStrips';
@@ -35,8 +38,11 @@ interface SelectedTimeframe {
35
38
  bounds: NumberDuo;
36
39
  }
37
40
 
38
- const timeframeParser = createParser<SelectedTimeframe>({
39
- parse: (value: string) => {
41
+ const TimeframeStateSerializer: OptionsCustom<SelectedTimeframe | undefined> = {
42
+ parse: (value: string | string[] | undefined) => {
43
+ if (value == null || value === '' || value === 'undefined' || Array.isArray(value)) {
44
+ return undefined;
45
+ }
40
46
  try {
41
47
  const [labelPart, boundsPart] = value.split('|');
42
48
  if (labelPart != null && boundsPart != null) {
@@ -55,13 +61,16 @@ const timeframeParser = createParser<SelectedTimeframe>({
55
61
  } catch {
56
62
  // Ignore parsing errors
57
63
  }
58
- return null;
64
+ return undefined;
59
65
  },
60
- serialize: (value: SelectedTimeframe) => {
66
+ stringify: (value: SelectedTimeframe | undefined) => {
67
+ if (value == null) {
68
+ return '';
69
+ }
61
70
  const labelsStr = value.labels.labels.map(l => `${l.name}:${l.value}`).join(',');
62
71
  return `${labelsStr}|${value.bounds[0]},${value.bounds[1]}`;
63
72
  },
64
- }).withOptions({history: 'replace'});
73
+ };
65
74
 
66
75
  interface ProfileFlameChartProps {
67
76
  samplesData?: SamplesData;
@@ -123,16 +132,14 @@ export const ProfileFlameChart = ({
123
132
  const {protoFilters} = useProfileFilters();
124
133
  const zoomControlsRef = useRef<HTMLDivElement>(null);
125
134
 
126
- const [selectedTimeframe, setSelectedTimeframe] = useQueryState(
127
- 'flamechart_timeframe',
128
- timeframeParser
129
- );
135
+ const [selectedTimeframe, setSelectedTimeframe] = useURLStateCustom<
136
+ SelectedTimeframe | undefined
137
+ >('flamechart_timeframe', TimeframeStateSerializer);
130
138
 
131
139
  // Read flamechart dimension from URL state to detect changes
132
- const [flamechartDimension] = useQueryState(
133
- 'flamechart_dimension',
134
- flamechartDimensionParser.withDefault([])
135
- );
140
+ const [flamechartDimension] = useURLState<string[]>('flamechart_dimension', {
141
+ alwaysReturnArray: true,
142
+ });
136
143
 
137
144
  // Reset selection when the parent time range (profileSource) changes
138
145
  const timeBoundsKey = boundsFromProfileSource(profileSource).join(',');
@@ -140,7 +147,7 @@ export const ProfileFlameChart = ({
140
147
  useEffect(() => {
141
148
  if (prevTimeBoundsKey.current !== timeBoundsKey) {
142
149
  prevTimeBoundsKey.current = timeBoundsKey;
143
- void setSelectedTimeframe(null);
150
+ setSelectedTimeframe(undefined);
144
151
  }
145
152
  }, [timeBoundsKey, setSelectedTimeframe]);
146
153
 
@@ -150,16 +157,16 @@ export const ProfileFlameChart = ({
150
157
  useEffect(() => {
151
158
  if (prevDimensionKey.current !== dimensionKey) {
152
159
  prevDimensionKey.current = dimensionKey;
153
- void setSelectedTimeframe(null);
160
+ setSelectedTimeframe(undefined);
154
161
  }
155
162
  }, [dimensionKey, setSelectedTimeframe]);
156
163
 
157
164
  // Handle timeframe selection from strips
158
165
  const handleSelectedTimeframe = (labels: LabelSet, bounds: NumberDuo | undefined): void => {
159
166
  if (bounds === undefined) {
160
- void setSelectedTimeframe(null);
167
+ setSelectedTimeframe(undefined);
161
168
  } else {
162
- void setSelectedTimeframe({labels, bounds});
169
+ setSelectedTimeframe({labels, bounds});
163
170
  }
164
171
  };
165
172
 
@@ -14,11 +14,10 @@
14
14
  import {Icon} from '@iconify/react';
15
15
  import {Table} from '@uwdata/flechette';
16
16
  import cx from 'classnames';
17
- import {useQueryState} from 'nuqs';
18
17
  import {Item, Menu, Separator, Submenu} from 'react-contexify';
19
18
  import {Tooltip} from 'react-tooltip';
20
19
 
21
- import {useParcaContext} from '@parca/components';
20
+ import {useParcaContext, useURLState} from '@parca/components';
22
21
  import {USER_PREFERENCES, useUserPreference} from '@parca/hooks';
23
22
  import {ProfileType} from '@parca/parser';
24
23
  import {TEST_IDS} from '@parca/test-utils';
@@ -26,8 +25,6 @@ import {getLastItem} from '@parca/utilities';
26
25
 
27
26
  import {useGraphTooltip} from '../../GraphTooltipArrow/useGraphTooltip';
28
27
  import {useGraphTooltipMetaInfo} from '../../GraphTooltipArrow/useGraphTooltipMetaInfo';
29
- import {stringParam} from '../../hooks/urlParsers';
30
- import {useDashboardItems} from '../../hooks/useDashboardItems';
31
28
  import {hexifyAddress, truncateString} from '../../utils';
32
29
 
33
30
  interface ContextMenuProps {
@@ -86,10 +83,12 @@ const ContextMenu = ({
86
83
  inlined,
87
84
  } = useGraphTooltipMetaInfo({table, row});
88
85
 
89
- const {dashboardItems, setDashboardItems} = useDashboardItems();
90
- const [_sandwichFunctionName, setSandwichFunctionName] = useQueryState(
91
- 'sandwich_function_name',
92
- stringParam
86
+ const [dashboardItems, setDashboardItems] = useURLState<string[]>('dashboard_items', {
87
+ alwaysReturnArray: true,
88
+ });
89
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
90
+ const [sandwichFunctionName, setSandwichFunctionName] = useURLState<string | undefined>(
91
+ 'sandwich_function_name'
93
92
  );
94
93
 
95
94
  if (contextMenuData === null) {
@@ -196,12 +195,12 @@ const ContextMenu = ({
196
195
  }
197
196
 
198
197
  if (dashboardItems.includes('sandwich')) {
199
- void setSandwichFunctionName(functionName);
198
+ setSandwichFunctionName(functionName);
200
199
  hideMenu();
201
200
  return;
202
201
  }
203
202
 
204
- void setSandwichFunctionName(functionName);
203
+ setSandwichFunctionName(functionName);
205
204
  setDashboardItems([...dashboardItems, 'sandwich']);
206
205
  hideMenu();
207
206
  }}
@@ -11,11 +11,11 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useEffect, useRef, useState} from 'react';
14
+ /* eslint-disable react-hooks/set-state-in-effect */
15
15
 
16
- import {useQueryState} from 'nuqs';
16
+ import {useEffect, useRef, useState} from 'react';
17
17
 
18
- import {stringParam} from '../../hooks/urlParsers';
18
+ import {useURLState} from '@parca/components';
19
19
 
20
20
  interface Props {
21
21
  text: string;
@@ -66,11 +66,12 @@ function calculateTruncatedText(
66
66
  }
67
67
 
68
68
  function TextWithEllipsis({text, x, y, width}: Props): JSX.Element {
69
+ 'use no memo';
69
70
  const textRef = useRef<SVGTextElement>(null);
70
71
  const [displayText, setDisplayText] = useState(text);
71
- const [alignFunctionName] = useQueryState('align_function_name', stringParam.withDefault('left'));
72
+ const [alignFunctionName] = useURLState('align_function_name');
72
73
 
73
- const showFunctionNameFromLeft = alignFunctionName === 'left';
74
+ const showFunctionNameFromLeft = alignFunctionName === 'left' || alignFunctionName === undefined;
74
75
 
75
76
  useEffect(() => {
76
77
  const textElement = textRef.current;
@@ -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);
@@ -15,11 +15,15 @@ import React, {LegacyRef, ReactNode, useCallback, useEffect, useMemo, useState}
15
15
 
16
16
  import cx from 'classnames';
17
17
  import {AnimatePresence, motion} from 'framer-motion';
18
- import {useQueryState} from 'nuqs';
19
18
  import {useMeasure} from 'react-use';
20
19
 
21
20
  import {FlamegraphArrow} from '@parca/client';
22
- import {FlameGraphSkeleton, SandwichFlameGraphSkeleton, useParcaContext} from '@parca/components';
21
+ import {
22
+ FlameGraphSkeleton,
23
+ SandwichFlameGraphSkeleton,
24
+ useParcaContext,
25
+ useURLState,
26
+ } from '@parca/components';
23
27
  import {ProfileType} from '@parca/parser';
24
28
  import {TEST_IDS, testId} from '@parca/test-utils';
25
29
  import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
@@ -29,7 +33,6 @@ import DiffLegend from '../ProfileView/components/DiffLegend';
29
33
  import {useProfileViewContext} from '../ProfileView/context/ProfileViewContext';
30
34
  import {useProfileMetadata} from '../ProfileView/hooks/useProfileMetadata';
31
35
  import {useVisualizationState} from '../ProfileView/hooks/useVisualizationState';
32
- import {boolParam} from '../hooks/urlParsers';
33
36
  import {FlameGraphArrow} from './FlameGraphArrow';
34
37
  import {CurrentPathFrame} from './FlameGraphArrow/utils';
35
38
 
@@ -134,8 +137,8 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
134
137
  // For non-delta profiles, like goroutines or memory, we want the profiles to be compared absolutely.
135
138
  const compareAbsoluteDefault = profileType?.delta === false ? 'true' : 'false';
136
139
 
137
- const [compareAbsolute] = useQueryState('compare_absolute', boolParam);
138
- const isCompareAbsolute = compareAbsolute ?? compareAbsoluteDefault === 'true';
140
+ const [compareAbsolute = compareAbsoluteDefault] = useURLState('compare_absolute');
141
+ const isCompareAbsolute = compareAbsolute === 'true';
139
142
 
140
143
  const mappingsListCount = useMemo(
141
144
  () => mappingsList.filter(m => m !== '').length,
@@ -177,7 +180,7 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
177
180
  // If there is only one mapping file, we want to color by filename by default.
178
181
  useEffect(() => {
179
182
  if (mappingsListCount === 1 && colorBy !== 'filename') {
180
- void setColorBy('filename');
183
+ setColorBy('filename');
181
184
  }
182
185
  // eslint-disable-next-line react-hooks/exhaustive-deps
183
186
  }, [mappingsListCount]);