@parca/profile 0.16.414 → 0.16.416

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 CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.16.416](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.415...@parca/profile@0.16.416) (2024-07-23)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.16.415](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.414...@parca/profile@0.16.415) (2024-07-23)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.16.414](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.413...@parca/profile@0.16.414) (2024-07-22)
7
15
 
8
16
  **Note:** Version bump only for package @parca/profile
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,EAA4B,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAOlF,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,GAAG,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAaD,eAAO,MAAM,sBAAsB,eAAgB,MAAM,GAAG,EAAE,KAAG,MAGhE,CAAC;AAgVF,QAAA,MAAM,eAAe,8CAIlB,oBAAoB,KAAG,GAAG,CAAC,OAkB7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGjD,OAAO,EAA4B,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAQlF,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,GAAG,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAaD,eAAO,MAAM,sBAAsB,eAAgB,MAAM,GAAG,EAAE,KAAG,MAGhE,CAAC;AAyWF,QAAA,MAAM,eAAe,8CAIlB,oBAAoB,KAAG,GAAG,CAAC,OAkB7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -18,6 +18,7 @@ import { createStore } from '@parca/store';
18
18
  import { capitalizeOnlyFirstLetter } from '@parca/utilities';
19
19
  import { ProfileSelectionFromParams, SuffixParams } from '..';
20
20
  import { useProfileTypes } from '../ProfileSelector';
21
+ import { sumByToParam, useSumByFromParams } from '../useSumBy';
21
22
  import ProfileExplorerCompare from './ProfileExplorerCompare';
22
23
  import ProfileExplorerSingle from './ProfileExplorerSingle';
23
24
  const ErrorContent = ({ errorMessage }) => {
@@ -38,6 +39,19 @@ const sanitizeDateRange = (time_selection_a, from_a, to_a) => {
38
39
  return { time_selection_a: range.getRangeKey(), from_a, to_a };
39
40
  };
40
41
  /* eslint-enable @typescript-eslint/naming-convention */
42
+ const filterEmptyParams = (o) => {
43
+ return Object.fromEntries(Object.entries(o)
44
+ .filter(([_, value]) => value !== '' && value !== undefined && (Array.isArray(value) ? value.length > 0 : true))
45
+ .map(([key, value]) => {
46
+ if (typeof value === 'string') {
47
+ return [key, value];
48
+ }
49
+ if (Array.isArray(value)) {
50
+ return [key, value];
51
+ }
52
+ return [key, value];
53
+ }));
54
+ };
41
55
  const filterSuffix = (o, suffix) => Object.fromEntries(Object.entries(o)
42
56
  .filter(([key]) => !key.endsWith(suffix))
43
57
  .map(([key, value]) => {
@@ -78,6 +92,8 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
78
92
  /* eslint-enable @typescript-eslint/naming-convention */
79
93
  const [profileA, setProfileA] = useState(null);
80
94
  const [profileB, setProfileB] = useState(null);
95
+ const sumByA = useSumByFromParams(sum_by_a);
96
+ const sumByB = useSumByFromParams(sum_by_b);
81
97
  useEffect(() => {
82
98
  const mergeFrom = merge_from_a ?? undefined;
83
99
  const mergeTo = merge_to_a ?? undefined;
@@ -134,7 +150,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
134
150
  from: parseInt(from_a),
135
151
  to: parseInt(to_a),
136
152
  timeSelection: time_selection_a,
137
- sumBy: sum_by_a,
153
+ sumBy: sumByA,
138
154
  };
139
155
  // Show the SingleProfileExplorer when not comparing
140
156
  if (compare_a !== 'true' && compare_b !== 'true') {
@@ -149,17 +165,18 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
149
165
  return navigateTo('/',
150
166
  // Filtering the _a suffix causes us to reset potential profile
151
167
  // selection when running a new query.
152
- {
168
+ filterEmptyParams({
153
169
  ...filterSuffix(queryParams, '_a'),
154
170
  ...{
155
171
  expression_a: encodeURIComponent(q.expression),
156
172
  from_a: q.from.toString(),
157
173
  to_a: q.to.toString(),
158
174
  time_selection_a: q.timeSelection,
175
+ sum_by_a: sumByToParam(q.sumBy),
159
176
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
160
177
  ...mergeParams,
161
178
  },
162
- });
179
+ }));
163
180
  };
164
181
  const selectProfile = (p) => {
165
182
  queryParams.expression_a = encodeURIComponent(queryParams.expression_a);
@@ -176,7 +193,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
176
193
  from: parseInt(from_b),
177
194
  to: parseInt(to_b),
178
195
  timeSelection: time_selection_b,
179
- sumBy: sum_by_b,
196
+ sumBy: sumByB,
180
197
  };
181
198
  const selectQueryA = (q) => {
182
199
  const mergeParams = q.mergeFrom !== undefined && q.mergeTo !== undefined
@@ -189,7 +206,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
189
206
  return navigateTo('/',
190
207
  // Filtering the _a suffix causes us to reset potential profile
191
208
  // selection when running a new query.
192
- {
209
+ filterEmptyParams({
193
210
  ...filterSuffix(queryParams, '_a'),
194
211
  ...{
195
212
  compare_a: 'true',
@@ -199,11 +216,12 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
199
216
  from_a: q.from.toString(),
200
217
  to_a: q.to.toString(),
201
218
  time_selection_a: q.timeSelection,
219
+ sum_by_a: sumByToParam(q.sumBy),
202
220
  filter_by_function: filter_by_function ?? '',
203
221
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
204
222
  ...mergeParams,
205
223
  },
206
- });
224
+ }));
207
225
  };
208
226
  const selectQueryB = (q) => {
209
227
  const mergeParams = q.mergeFrom !== undefined && q.mergeTo !== undefined
@@ -216,7 +234,7 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
216
234
  return navigateTo('/',
217
235
  // Filtering the _b suffix causes us to reset potential profile
218
236
  // selection when running a new query.
219
- {
237
+ filterEmptyParams({
220
238
  ...filterSuffix(queryParams, '_b'),
221
239
  ...{
222
240
  compare_b: 'true',
@@ -226,11 +244,12 @@ const ProfileExplorerApp = ({ queryClient, queryParams, navigateTo, }) => {
226
244
  from_b: q.from.toString(),
227
245
  to_b: q.to.toString(),
228
246
  time_selection_b: q.timeSelection,
247
+ sum_by_b: sumByToParam(q.sumBy),
229
248
  filter_by_function: filter_by_function ?? '',
230
249
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
231
250
  ...mergeParams,
232
251
  },
233
- });
252
+ }));
234
253
  };
235
254
  const closeProfile = (card) => {
236
255
  let newQueryParameters = queryParams;
@@ -53,7 +53,7 @@ export const useQueryRange = (client, queryExpression, start, end, sumBy, skip =
53
53
  }
54
54
  }, [stepCountStr, defaultStepCount, setStepCount]);
55
55
  const { data, isLoading, error } = useGrpcQuery({
56
- key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
56
+ key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
57
57
  queryFn: async () => {
58
58
  const stepDuration = getStepDuration(start, end, stepCount);
59
59
  const { response } = await client.queryRange({
@@ -29,6 +29,6 @@ export interface IProfileTypesResult {
29
29
  error?: RpcError;
30
30
  }
31
31
  export declare const useProfileTypes: (client: QueryServiceClient) => IProfileTypesResult;
32
- declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, suffix, }: ProfileSelectorProps) => JSX.Element;
32
+ declare const ProfileSelector: ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }: ProfileSelectorProps) => JSX.Element;
33
33
  export default ProfileSelector;
34
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQ5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,qJAWlB,oBAAoB,KAAG,GAAG,CAAC,OAiU7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAGlD,OAAO,EAAQ,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAY9E,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAyB,gBAAgB,EAAC,MAAM,IAAI,CAAC;AAQ5D,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,oBAAoB;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,WAAY,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,6IAUlB,oBAAoB,KAAG,GAAG,CAAC,OA6U7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { useEffect, useMemo, useState } from 'react';
14
+ import { useEffect, useMemo, useRef, useState } from 'react';
15
15
  import Select from 'react-select';
16
16
  import { Button, ButtonGroup, DateTimeRange, DateTimeRangePicker, IconButton, useGrpcMetadata, useParcaContext, } from '@parca/components';
17
17
  import { CloseIcon } from '@parca/icons';
@@ -21,7 +21,7 @@ import MatchersInput, { useLabelNames } from '../MatchersInput/index';
21
21
  import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
22
22
  import ProfileMetricsGraph, { ProfileMetricsEmptyState } from '../ProfileMetricsGraph';
23
23
  import ProfileTypeSelector from '../ProfileTypeSelector/index';
24
- import { useSumBy } from '../useSumBy';
24
+ import { useDefaultSumBy, useSumBySelection } from '../useSumBy';
25
25
  import { useAutoQuerySelector } from './useAutoQuerySelector';
26
26
  export const useProfileTypes = (client) => {
27
27
  const [result, setResult] = useState(undefined);
@@ -40,10 +40,11 @@ export const useProfileTypes = (client) => {
40
40
  }, [client, metadata, loading]);
41
41
  return { loading, data: result, error };
42
42
  };
43
- const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, suffix = '', }) => {
43
+ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQuery, closeProfile, enforcedProfileName, profileSelection, comparing, navigateTo, }) => {
44
44
  const { loading: profileTypesLoading, data: profileTypesData, error, } = useProfileTypes(queryClient);
45
45
  const { heightStyle } = useMetricsGraphDimensions(comparing);
46
46
  const { viewComponent } = useParcaContext();
47
+ const sumByRef = useRef(null);
47
48
  const [timeRangeSelection, setTimeRangeSelection] = useState(DateTimeRange.fromRangeKey(querySelection.timeSelection, querySelection.from, querySelection.to));
48
49
  const [queryExpressionString, setQueryExpressionString] = useState(querySelection.expression);
49
50
  const profileType = useMemo(() => {
@@ -62,14 +63,10 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
62
63
  ? []
63
64
  : selectedLabelNamesResult.response.labelNames;
64
65
  }, [selectedLabelNamesResult]);
65
- const [sumBy, setSumBy, { userSelectedSumBy, isLoading: isSumByLoading }] = useSumBy(selectedProfileType, selectedLabelNamesLoading, selectedLabels, {
66
- urlParamKey: 'sum_by' + suffix,
67
- });
68
- const [sumBySelection, setSumBySelection, { isLoading: isSumBySelectionLoading }] = useSumBy(profileType, labelNamesLoading, labels, {
69
- urlParamKey: 'sum_by_selection' + suffix,
70
- withURLUpdate: false,
71
- defaultValue: userSelectedSumBy,
66
+ const [sumBySelection, setUserSumBySelection, { isLoading: sumBySelectionLoading }] = useSumBySelection(profileType, labelNamesLoading, labels, {
67
+ defaultValue: querySelection.sumBy,
72
68
  });
69
+ const { defaultSumBy, isLoading: defaultSumByLoading } = useDefaultSumBy(selectedProfileType, selectedLabelNamesLoading, selectedLabels);
73
70
  useEffect(() => {
74
71
  if (enforcedProfileName !== '') {
75
72
  const [q, changed] = Query.parse(querySelection.expression).setProfileName(enforcedProfileName);
@@ -88,9 +85,6 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
88
85
  const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(queryExpressionString);
89
86
  const selectedProfileName = query.profileName();
90
87
  const setNewQueryExpression = (expr, updateTs = false) => {
91
- if (!isSumBySelectionLoading) {
92
- setSumBy(sumBySelection);
93
- }
94
88
  const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
95
89
  const delta = query.profileType().delta;
96
90
  const from = timeRangeSelection.getFromMs(updateTs);
@@ -106,6 +100,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
106
100
  from,
107
101
  to,
108
102
  timeSelection: timeRangeSelection.getRangeKey(),
103
+ sumBy: sumBySelection,
109
104
  ...mergeParams,
110
105
  });
111
106
  };
@@ -165,24 +160,41 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
165
160
  profileTypesData,
166
161
  setProfileName,
167
162
  setQueryExpression,
168
- querySelection: { ...querySelection, sumBy },
163
+ querySelection: { ...querySelection, sumBy: sumBySelection },
169
164
  navigateTo,
165
+ loading: sumBySelectionLoading,
170
166
  });
171
167
  const searchDisabled = queryExpressionString === undefined ||
172
168
  queryExpressionString === '' ||
173
169
  queryExpressionString === '{}';
174
170
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex gap-2", children: [_jsxs("div", { className: "flex w-full flex-wrap content-start items-center gap-2", children: [_jsxs("div", { className: "pb-6", children: [_jsx("label", { className: "text-xs", children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: error, disabled: viewComponent?.disableProfileTypesDropdown })] }), _jsxs("div", { className: "w-full flex-1 pb-6", children: [_jsxs("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: [_jsx("label", { className: "text-xs", children: "Query" }), (query.matchers.length > 0 || query.inputMatcherString.length > 0) &&
175
- viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })] }), _jsxs("div", { className: "pb-6", children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-80", classNamePrefix: "parca-select", value: sumBySelection.map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
176
- setSumBySelection(selectedOptions.map(option => option.value));
171
+ viewComponent !== undefined && _jsx("div", { children: viewComponent?.createViewComponent })] }), _jsx(MatchersInput, { queryClient: queryClient, setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName })] }), _jsxs("div", { className: "pb-6", children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", children: "Sum by" }) }), _jsx(Select, { defaultValue: [], isMulti: true, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-80", classNamePrefix: "parca-select", value: (sumBySelection ?? []).map(sumBy => ({ label: sumBy, value: sumBy })), onChange: selectedOptions => {
172
+ setUserSumBySelection(selectedOptions.map(option => option.value));
177
173
  }, placeholder: "Labels...", styles: {
178
174
  indicatorSeparator: () => ({ display: 'none' }),
175
+ }, isDisabled: !profileType.delta, ref: sumByRef, onKeyDown: e => {
176
+ const currentRef = sumByRef.current;
177
+ if (currentRef == null) {
178
+ return;
179
+ }
180
+ const inputRef = currentRef.inputRef;
181
+ if (inputRef == null) {
182
+ return;
183
+ }
184
+ if (e.key === 'Enter' &&
185
+ inputRef.value === '' &&
186
+ currentRef.state.focusedOptionId === null // menu is not open
187
+ ) {
188
+ setQueryExpression(true);
189
+ currentRef.blur();
190
+ }
179
191
  } })] }), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection }), _jsx(ButtonGroup, { children: _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
180
192
  e.preventDefault();
181
193
  setQueryExpression(true);
182
194
  }, id: "h-matcher-search-button", children: "Search" }) })] }), _jsx("div", { children: comparing && _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) })] }), _jsx("div", { className: "rounded bg-white shadow dark:border-gray-500 dark:bg-gray-700", children: _jsx("div", { style: { height: heightStyle }, children: querySelection.expression !== undefined &&
183
195
  querySelection.expression.length > 0 &&
184
196
  querySelection.from !== undefined &&
185
- querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: sumBy, sumByLoading: isSumByLoading, setTimeRange: (range) => {
197
+ querySelection.to !== undefined ? (_jsx("div", { className: "p-2", children: _jsx(ProfileMetricsGraph, { queryClient: queryClient, queryExpression: querySelection.expression, from: querySelection.from, to: querySelection.to, profile: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], sumByLoading: defaultSumByLoading, setTimeRange: (range) => {
186
198
  const from = range.getFromMs();
187
199
  const to = range.getToMs();
188
200
  let mergedProfileParams = {};
@@ -8,7 +8,8 @@ interface Props {
8
8
  setQueryExpression: () => void;
9
9
  querySelection: QuerySelection;
10
10
  navigateTo: NavigateFunction;
11
+ loading: boolean;
11
12
  }
12
- export declare const useAutoQuerySelector: ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, }: Props) => void;
13
+ export declare const useAutoQuerySelector: ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, loading, }: Props) => void;
13
14
  export {};
14
15
  //# sourceMappingURL=useAutoQuerySelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoQuerySelector.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/useAutoQuerySelector.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,eAAO,MAAM,oBAAoB,+GAO9B,KAAK,KAAG,IAkIV,CAAC"}
1
+ {"version":3,"file":"useAutoQuerySelector.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/useAutoQuerySelector.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,oBAAoB,EAAC,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACnD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,oBAAoB,wHAQ9B,KAAK,KAAG,IAuIV,CAAC"}
@@ -14,13 +14,16 @@ import { useEffect } from 'react';
14
14
  import { selectAutoQuery, setAutoQuery, useAppDispatch, useAppSelector } from '@parca/store';
15
15
  import { ProfileSelectionFromParams, SuffixParams } from '..';
16
16
  import { constructProfileName } from '../ProfileTypeSelector';
17
- export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, }) => {
17
+ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, setProfileName, setQueryExpression, querySelection, navigateTo, loading, }) => {
18
18
  const autoQuery = useAppSelector(selectAutoQuery);
19
19
  const dispatch = useAppDispatch();
20
20
  const queryParams = new URLSearchParams(location.search);
21
21
  const comparing = queryParams.get('comparing') === 'true';
22
22
  const expressionA = queryParams.get('expression_a');
23
23
  useEffect(() => {
24
+ if (loading) {
25
+ return;
26
+ }
24
27
  if (comparing && expressionA !== null && expressionA !== undefined) {
25
28
  if (querySelection.expression === undefined) {
26
29
  return;
@@ -62,7 +65,7 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
62
65
  dashboard_items: ['icicle'],
63
66
  });
64
67
  }
65
- }, [comparing, querySelection, navigateTo, expressionA, dispatch]);
68
+ }, [comparing, querySelection, navigateTo, expressionA, dispatch, loading]);
66
69
  // Effect to load some initial data on load when is no selection
67
70
  useEffect(() => {
68
71
  void (async () => {
@@ -105,7 +108,8 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
105
108
  if (autoQuery !== 'true' ||
106
109
  profileTypesData?.types == null ||
107
110
  profileTypesData.types.length < 1 ||
108
- selectedProfileName.length === 0) {
111
+ selectedProfileName.length === 0 ||
112
+ loading) {
109
113
  return;
110
114
  }
111
115
  setQueryExpression();
@@ -118,5 +122,6 @@ export const useAutoQuerySelector = ({ selectedProfileName, profileTypesData, se
118
122
  setProfileName,
119
123
  dispatch,
120
124
  selectedProfileName,
125
+ loading,
121
126
  ]);
122
127
  };
@@ -1,11 +1,14 @@
1
1
  import { ProfileType } from '@parca/parser';
2
2
  export declare const DEFAULT_EMPTY_SUM_BY: string[];
3
- export declare const useSumBy: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, { urlParamKey, withURLUpdate, defaultValue, }?: {
4
- urlParamKey?: string;
5
- withURLUpdate?: boolean;
3
+ export declare const useSumBySelection: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined, { defaultValue, }?: {
6
4
  defaultValue?: string[];
7
- }) => [string[], (labels: string[]) => void, {
8
- userSelectedSumBy: string[] | undefined;
5
+ }) => [string[] | undefined, (labels: string[]) => void, {
9
6
  isLoading: boolean;
10
7
  }];
8
+ export declare const useDefaultSumBy: (profileType: ProfileType | undefined, labelNamesLoading: boolean, labels: string[] | undefined) => {
9
+ defaultSumBy: string[] | undefined;
10
+ isLoading: boolean;
11
+ };
12
+ export declare const useSumByFromParams: (param: string | string[] | undefined) => string[] | undefined;
13
+ export declare const sumByToParam: (sumBy: string[] | undefined) => string | string[] | undefined;
11
14
  //# sourceMappingURL=useSumBy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AAyBjD,eAAO,MAAM,QAAQ,gBACN,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,kDAKzB;IACD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,KACA,CACD,MAAM,EAAE,EACR,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IAAC,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAC,CA4D9D,CAAC"}
1
+ {"version":3,"file":"useSumBy.d.ts","sourceRoot":"","sources":["../src/useSumBy.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,MAAM,EAAO,CAAC;AA6BjD,eAAO,MAAM,iBAAiB,gBACf,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,sBAGzB;IACD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB,KACA,CACD,MAAM,EAAE,GAAG,SAAS,EACpB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,EAC1B;IACE,SAAS,EAAE,OAAO,CAAC;CACpB,CAsCF,CAAC;AAEF,eAAO,MAAM,eAAe,gBACb,WAAW,GAAG,SAAS,qBACjB,OAAO,UAClB,MAAM,EAAE,GAAG,SAAS,KAC3B;IAAC,YAAY,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAMzD,CAAC;AAkBF,eAAO,MAAM,kBAAkB,UAAW,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EAAE,GAAG,SAMpF,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAU9E,CAAC"}
package/dist/useSumBy.js CHANGED
@@ -10,13 +10,15 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
- import { useCallback, useMemo } from 'react';
14
- import { useParcaContext, useURLState } from '@parca/components';
13
+ import { useCallback, useMemo, useState } from 'react';
15
14
  export const DEFAULT_EMPTY_SUM_BY = [];
16
15
  const getDefaultSumBy = (profile, labels) => {
17
16
  if (profile === undefined || labels === undefined) {
18
17
  return undefined;
19
18
  }
19
+ if (!profile.delta) {
20
+ return DEFAULT_EMPTY_SUM_BY;
21
+ }
20
22
  if (labels.includes('comm')) {
21
23
  return ['comm'];
22
24
  }
@@ -28,47 +30,62 @@ const getDefaultSumBy = (profile, labels) => {
28
30
  }
29
31
  return undefined;
30
32
  };
31
- export const useSumBy = (profileType, labelNamesLoading, labels, { urlParamKey = 'sum_by', withURLUpdate = true, defaultValue, } = {}) => {
32
- const { navigateTo } = useParcaContext();
33
- const [userSelectedSumByParam, setUserSelectedSumByParam] = useURLState({
34
- param: urlParamKey,
35
- navigateTo,
36
- withURLUpdate,
37
- });
38
- const userSelectedSumBy = useMemo(() => {
39
- if (userSelectedSumByParam?.length === 0) {
40
- return undefined;
41
- }
42
- if (userSelectedSumByParam === '__none__') {
43
- return [];
44
- }
45
- if (userSelectedSumByParam === undefined && defaultValue !== undefined) {
46
- return defaultValue;
47
- }
48
- if (typeof userSelectedSumByParam === 'string') {
49
- return [userSelectedSumByParam];
50
- }
51
- return userSelectedSumByParam;
52
- // eslint-disable-next-line react-hooks/exhaustive-deps
53
- }, [userSelectedSumByParam]);
54
- const setUserSelectedSumBy = useCallback((sumBy) => {
55
- if (sumBy.length === 0) {
56
- setUserSelectedSumByParam('__none__');
57
- return;
58
- }
59
- if (sumBy.length === 1) {
60
- // Handle this separately to take care of the empty string scenario
61
- setUserSelectedSumByParam(sumBy[0]);
62
- return;
63
- }
64
- setUserSelectedSumByParam(sumBy);
65
- }, [setUserSelectedSumByParam]);
33
+ export const useSumBySelection = (profileType, labelNamesLoading, labels, { defaultValue, } = {}) => {
34
+ const [userSelectedSumBy, setUserSelectedSumBy] = useState(profileType != null ? { [profileType.toString()]: defaultValue } : {});
35
+ const setSumBy = useCallback((sumBy) => {
36
+ setUserSelectedSumBy(prev => {
37
+ if (profileType == null) {
38
+ return prev;
39
+ }
40
+ return {
41
+ ...prev,
42
+ [profileType.toString()]: sumBy,
43
+ };
44
+ });
45
+ }, [setUserSelectedSumBy, profileType]);
46
+ const { defaultSumBy } = useDefaultSumBy(profileType, labelNamesLoading, labels);
47
+ let sumBy = userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
48
+ if (profileType?.delta !== true) {
49
+ sumBy = DEFAULT_EMPTY_SUM_BY;
50
+ }
51
+ return [
52
+ labelNamesLoading ? undefined : sumBy,
53
+ setSumBy,
54
+ {
55
+ isLoading: labelNamesLoading,
56
+ },
57
+ ];
58
+ };
59
+ export const useDefaultSumBy = (profileType, labelNamesLoading, labels) => {
66
60
  const defaultSumBy = useMemo(() => {
67
61
  return getDefaultSumBy(profileType, labels);
68
62
  }, [profileType, labels]);
69
- let sumBy = userSelectedSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
70
- if (profileType?.delta !== true) {
71
- sumBy = DEFAULT_EMPTY_SUM_BY;
63
+ return { defaultSumBy, isLoading: labelNamesLoading };
64
+ };
65
+ const getSumByFromParam = (param) => {
66
+ if (param?.length === 0) {
67
+ return undefined;
68
+ }
69
+ if (param === '__none__') {
70
+ return [];
71
+ }
72
+ if (typeof param === 'string') {
73
+ return [param];
74
+ }
75
+ return param;
76
+ };
77
+ export const useSumByFromParams = (param) => {
78
+ const sumBy = useMemo(() => {
79
+ return getSumByFromParam(param);
80
+ }, [param]);
81
+ return sumBy;
82
+ };
83
+ export const sumByToParam = (sumBy) => {
84
+ if (sumBy === undefined) {
85
+ return undefined;
86
+ }
87
+ if (sumBy.length === 0) {
88
+ return '__none__';
72
89
  }
73
- return [sumBy, setUserSelectedSumBy, { userSelectedSumBy, isLoading: labelNamesLoading }];
90
+ return sumBy;
74
91
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.414",
3
+ "version": "0.16.416",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
@@ -73,5 +73,5 @@
73
73
  "access": "public",
74
74
  "registry": "https://registry.npmjs.org/"
75
75
  },
76
- "gitHead": "7344e3c5687b4d9a5c93b6c44494592726fcba23"
76
+ "gitHead": "b57a9ad4f98b2ea30eca63611e487fb94dbbe106"
77
77
  }
@@ -22,6 +22,7 @@ import {capitalizeOnlyFirstLetter, type NavigateFunction} from '@parca/utilities
22
22
 
23
23
  import {ProfileSelection, ProfileSelectionFromParams, SuffixParams} from '..';
24
24
  import {QuerySelection, useProfileTypes} from '../ProfileSelector';
25
+ import {sumByToParam, useSumByFromParams} from '../useSumBy';
25
26
  import ProfileExplorerCompare from './ProfileExplorerCompare';
26
27
  import ProfileExplorerSingle from './ProfileExplorerSingle';
27
28
 
@@ -64,6 +65,25 @@ const sanitizeDateRange = (
64
65
  };
65
66
  /* eslint-enable @typescript-eslint/naming-convention */
66
67
 
68
+ const filterEmptyParams = (o: Record<string, any>): Record<string, any> => {
69
+ return Object.fromEntries(
70
+ Object.entries(o)
71
+ .filter(
72
+ ([_, value]) =>
73
+ value !== '' && value !== undefined && (Array.isArray(value) ? value.length > 0 : true)
74
+ )
75
+ .map(([key, value]) => {
76
+ if (typeof value === 'string') {
77
+ return [key, value];
78
+ }
79
+ if (Array.isArray(value)) {
80
+ return [key, value];
81
+ }
82
+ return [key, value];
83
+ })
84
+ );
85
+ };
86
+
67
87
  const filterSuffix = (
68
88
  o: {[key: string]: string | string[] | undefined},
69
89
  suffix: string
@@ -148,6 +168,9 @@ const ProfileExplorerApp = ({
148
168
  const [profileA, setProfileA] = useState<ProfileSelection | null>(null);
149
169
  const [profileB, setProfileB] = useState<ProfileSelection | null>(null);
150
170
 
171
+ const sumByA = useSumByFromParams(sum_by_a);
172
+ const sumByB = useSumByFromParams(sum_by_b);
173
+
151
174
  useEffect(() => {
152
175
  const mergeFrom = merge_from_a ?? undefined;
153
176
  const mergeTo = merge_to_a ?? undefined;
@@ -225,7 +248,7 @@ const ProfileExplorerApp = ({
225
248
  from: parseInt(from_a as string),
226
249
  to: parseInt(to_a as string),
227
250
  timeSelection: time_selection_a as string,
228
- sumBy: sum_by_a as string[],
251
+ sumBy: sumByA,
229
252
  };
230
253
 
231
254
  // Show the SingleProfileExplorer when not comparing
@@ -243,17 +266,18 @@ const ProfileExplorerApp = ({
243
266
  '/',
244
267
  // Filtering the _a suffix causes us to reset potential profile
245
268
  // selection when running a new query.
246
- {
269
+ filterEmptyParams({
247
270
  ...filterSuffix(queryParams, '_a'),
248
271
  ...{
249
272
  expression_a: encodeURIComponent(q.expression),
250
273
  from_a: q.from.toString(),
251
274
  to_a: q.to.toString(),
252
275
  time_selection_a: q.timeSelection,
276
+ sum_by_a: sumByToParam(q.sumBy),
253
277
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
254
278
  ...mergeParams,
255
279
  },
256
- }
280
+ })
257
281
  );
258
282
  };
259
283
 
@@ -283,7 +307,7 @@ const ProfileExplorerApp = ({
283
307
  from: parseInt(from_b as string),
284
308
  to: parseInt(to_b as string),
285
309
  timeSelection: time_selection_b as string,
286
- sumBy: sum_by_b as string[],
310
+ sumBy: sumByB,
287
311
  };
288
312
 
289
313
  const selectQueryA = (q: QuerySelection): void => {
@@ -299,7 +323,7 @@ const ProfileExplorerApp = ({
299
323
  '/',
300
324
  // Filtering the _a suffix causes us to reset potential profile
301
325
  // selection when running a new query.
302
- {
326
+ filterEmptyParams({
303
327
  ...filterSuffix(queryParams, '_a'),
304
328
  ...{
305
329
  compare_a: 'true',
@@ -309,11 +333,12 @@ const ProfileExplorerApp = ({
309
333
  from_a: q.from.toString(),
310
334
  to_a: q.to.toString(),
311
335
  time_selection_a: q.timeSelection,
336
+ sum_by_a: sumByToParam(q.sumBy),
312
337
  filter_by_function: filter_by_function ?? '',
313
338
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
314
339
  ...mergeParams,
315
340
  },
316
- }
341
+ })
317
342
  );
318
343
  };
319
344
 
@@ -330,7 +355,7 @@ const ProfileExplorerApp = ({
330
355
  '/',
331
356
  // Filtering the _b suffix causes us to reset potential profile
332
357
  // selection when running a new query.
333
- {
358
+ filterEmptyParams({
334
359
  ...filterSuffix(queryParams, '_b'),
335
360
  ...{
336
361
  compare_b: 'true',
@@ -340,11 +365,12 @@ const ProfileExplorerApp = ({
340
365
  from_b: q.from.toString(),
341
366
  to_b: q.to.toString(),
342
367
  time_selection_b: q.timeSelection,
368
+ sum_by_b: sumByToParam(q.sumBy),
343
369
  filter_by_function: filter_by_function ?? '',
344
370
  dashboard_items: dashboard_items ?? DEFAULT_DASHBOARD_ITEMS,
345
371
  ...mergeParams,
346
372
  },
347
- }
373
+ })
348
374
  );
349
375
  };
350
376
 
@@ -122,7 +122,7 @@ export const useQueryRange = (
122
122
  }, [stepCountStr, defaultStepCount, setStepCount]);
123
123
 
124
124
  const {data, isLoading, error} = useGrpcQuery<QueryRangeResponse | undefined>({
125
- key: ['query-range', queryExpression, start, end, sumBy.join(','), stepCount, metadata],
125
+ key: ['query-range', queryExpression, start, end, (sumBy ?? []).join(','), stepCount, metadata],
126
126
  queryFn: async () => {
127
127
  const stepDuration = getStepDuration(start, end, stepCount);
128
128
  const {response} = await client.queryRange(
@@ -11,10 +11,10 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {useEffect, useMemo, useState} from 'react';
14
+ import React, {useEffect, useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import {RpcError} from '@protobuf-ts/runtime-rpc';
17
- import Select from 'react-select';
17
+ import Select, {type SelectInstance} from 'react-select';
18
18
 
19
19
  import {Label, ProfileTypesResponse, QueryServiceClient} from '@parca/client';
20
20
  import {
@@ -35,7 +35,7 @@ import MatchersInput, {useLabelNames} from '../MatchersInput/index';
35
35
  import {useMetricsGraphDimensions} from '../MetricsGraph/useMetricsGraphDimensions';
36
36
  import ProfileMetricsGraph, {ProfileMetricsEmptyState} from '../ProfileMetricsGraph';
37
37
  import ProfileTypeSelector from '../ProfileTypeSelector/index';
38
- import {useSumBy} from '../useSumBy';
38
+ import {useDefaultSumBy, useSumBySelection} from '../useSumBy';
39
39
  import {useAutoQuerySelector} from './useAutoQuerySelector';
40
40
 
41
41
  export interface QuerySelection {
@@ -97,7 +97,6 @@ const ProfileSelector = ({
97
97
  profileSelection,
98
98
  comparing,
99
99
  navigateTo,
100
- suffix = '',
101
100
  }: ProfileSelectorProps): JSX.Element => {
102
101
  const {
103
102
  loading: profileTypesLoading,
@@ -106,6 +105,7 @@ const ProfileSelector = ({
106
105
  } = useProfileTypes(queryClient);
107
106
  const {heightStyle} = useMetricsGraphDimensions(comparing);
108
107
  const {viewComponent} = useParcaContext();
108
+ const sumByRef = useRef(null);
109
109
 
110
110
  const [timeRangeSelection, setTimeRangeSelection] = useState(
111
111
  DateTimeRange.fromRangeKey(querySelection.timeSelection, querySelection.from, querySelection.to)
@@ -137,24 +137,15 @@ const ProfileSelector = ({
137
137
  : selectedLabelNamesResult.response.labelNames;
138
138
  }, [selectedLabelNamesResult]);
139
139
 
140
- const [sumBy, setSumBy, {userSelectedSumBy, isLoading: isSumByLoading}] = useSumBy(
140
+ const [sumBySelection, setUserSumBySelection, {isLoading: sumBySelectionLoading}] =
141
+ useSumBySelection(profileType, labelNamesLoading, labels, {
142
+ defaultValue: querySelection.sumBy,
143
+ });
144
+
145
+ const {defaultSumBy, isLoading: defaultSumByLoading} = useDefaultSumBy(
141
146
  selectedProfileType,
142
147
  selectedLabelNamesLoading,
143
- selectedLabels,
144
- {
145
- urlParamKey: 'sum_by' + suffix,
146
- }
147
- );
148
-
149
- const [sumBySelection, setSumBySelection, {isLoading: isSumBySelectionLoading}] = useSumBy(
150
- profileType,
151
- labelNamesLoading,
152
- labels,
153
- {
154
- urlParamKey: 'sum_by_selection' + suffix,
155
- withURLUpdate: false,
156
- defaultValue: userSelectedSumBy,
157
- }
148
+ selectedLabels
158
149
  );
159
150
 
160
151
  useEffect(() => {
@@ -181,9 +172,6 @@ const ProfileSelector = ({
181
172
  const selectedProfileName = query.profileName();
182
173
 
183
174
  const setNewQueryExpression = (expr: string, updateTs = false): void => {
184
- if (!isSumBySelectionLoading) {
185
- setSumBy(sumBySelection);
186
- }
187
175
  const query = enforcedProfileName !== '' ? enforcedProfileNameQuery() : Query.parse(expr);
188
176
  const delta = query.profileType().delta;
189
177
  const from = timeRangeSelection.getFromMs(updateTs);
@@ -200,6 +188,7 @@ const ProfileSelector = ({
200
188
  from,
201
189
  to,
202
190
  timeSelection: timeRangeSelection.getRangeKey(),
191
+ sumBy: sumBySelection,
203
192
  ...mergeParams,
204
193
  });
205
194
  };
@@ -269,8 +258,9 @@ const ProfileSelector = ({
269
258
  profileTypesData,
270
259
  setProfileName,
271
260
  setQueryExpression,
272
- querySelection: {...querySelection, sumBy},
261
+ querySelection: {...querySelection, sumBy: sumBySelection},
273
262
  navigateTo,
263
+ loading: sumBySelectionLoading,
274
264
  });
275
265
 
276
266
  const searchDisabled =
@@ -318,14 +308,35 @@ const ProfileSelector = ({
318
308
  options={labels.map(label => ({label, value: label}))}
319
309
  className="parca-select-container text-sm w-80"
320
310
  classNamePrefix="parca-select"
321
- value={sumBySelection.map(sumBy => ({label: sumBy, value: sumBy}))}
311
+ value={(sumBySelection ?? []).map(sumBy => ({label: sumBy, value: sumBy}))}
322
312
  onChange={selectedOptions => {
323
- setSumBySelection(selectedOptions.map(option => option.value));
313
+ setUserSumBySelection(selectedOptions.map(option => option.value));
324
314
  }}
325
315
  placeholder="Labels..."
326
316
  styles={{
327
317
  indicatorSeparator: () => ({display: 'none'}),
328
318
  }}
319
+ isDisabled={!profileType.delta}
320
+ ref={sumByRef}
321
+ onKeyDown={e => {
322
+ const currentRef = sumByRef.current as unknown as SelectInstance | null;
323
+ if (currentRef == null) {
324
+ return;
325
+ }
326
+ const inputRef = currentRef.inputRef;
327
+ if (inputRef == null) {
328
+ return;
329
+ }
330
+
331
+ if (
332
+ e.key === 'Enter' &&
333
+ inputRef.value === '' &&
334
+ currentRef.state.focusedOptionId === null // menu is not open
335
+ ) {
336
+ setQueryExpression(true);
337
+ currentRef.blur();
338
+ }
339
+ }}
329
340
  />
330
341
  </div>
331
342
  <DateTimeRangePicker
@@ -361,8 +372,8 @@ const ProfileSelector = ({
361
372
  to={querySelection.to}
362
373
  profile={profileSelection}
363
374
  comparing={comparing}
364
- sumBy={sumBy}
365
- sumByLoading={isSumByLoading}
375
+ sumBy={querySelection.sumBy ?? defaultSumBy ?? []}
376
+ sumByLoading={defaultSumByLoading}
366
377
  setTimeRange={(range: DateTimeRange) => {
367
378
  const from = range.getFromMs();
368
379
  const to = range.getToMs();
@@ -28,6 +28,7 @@ interface Props {
28
28
  setQueryExpression: () => void;
29
29
  querySelection: QuerySelection;
30
30
  navigateTo: NavigateFunction;
31
+ loading: boolean;
31
32
  }
32
33
 
33
34
  export const useAutoQuerySelector = ({
@@ -37,6 +38,7 @@ export const useAutoQuerySelector = ({
37
38
  setQueryExpression,
38
39
  querySelection,
39
40
  navigateTo,
41
+ loading,
40
42
  }: Props): void => {
41
43
  const autoQuery = useAppSelector(selectAutoQuery);
42
44
  const dispatch = useAppDispatch();
@@ -46,6 +48,9 @@ export const useAutoQuerySelector = ({
46
48
  const expressionA = queryParams.get('expression_a');
47
49
 
48
50
  useEffect(() => {
51
+ if (loading) {
52
+ return;
53
+ }
49
54
  if (comparing && expressionA !== null && expressionA !== undefined) {
50
55
  if (querySelection.expression === undefined) {
51
56
  return;
@@ -98,7 +103,7 @@ export const useAutoQuerySelector = ({
98
103
  dashboard_items: ['icicle'],
99
104
  });
100
105
  }
101
- }, [comparing, querySelection, navigateTo, expressionA, dispatch]);
106
+ }, [comparing, querySelection, navigateTo, expressionA, dispatch, loading]);
102
107
 
103
108
  // Effect to load some initial data on load when is no selection
104
109
  useEffect(() => {
@@ -152,7 +157,8 @@ export const useAutoQuerySelector = ({
152
157
  autoQuery !== 'true' ||
153
158
  profileTypesData?.types == null ||
154
159
  profileTypesData.types.length < 1 ||
155
- selectedProfileName.length === 0
160
+ selectedProfileName.length === 0 ||
161
+ loading
156
162
  ) {
157
163
  return;
158
164
  }
@@ -166,5 +172,6 @@ export const useAutoQuerySelector = ({
166
172
  setProfileName,
167
173
  dispatch,
168
174
  selectedProfileName,
175
+ loading,
169
176
  ]);
170
177
  };
package/src/useSumBy.ts CHANGED
@@ -11,9 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import {useCallback, useMemo} from 'react';
14
+ import {useCallback, useMemo, useState} from 'react';
15
15
 
16
- import {useParcaContext, useURLState} from '@parca/components';
17
16
  import {ProfileType} from '@parca/parser';
18
17
 
19
18
  export const DEFAULT_EMPTY_SUM_BY: string[] = [];
@@ -26,6 +25,10 @@ const getDefaultSumBy = (
26
25
  return undefined;
27
26
  }
28
27
 
28
+ if (!profile.delta) {
29
+ return DEFAULT_EMPTY_SUM_BY;
30
+ }
31
+
29
32
  if (labels.includes('comm')) {
30
33
  return ['comm'];
31
34
  }
@@ -41,80 +44,104 @@ const getDefaultSumBy = (
41
44
  return undefined;
42
45
  };
43
46
 
44
- export const useSumBy = (
47
+ export const useSumBySelection = (
45
48
  profileType: ProfileType | undefined,
46
49
  labelNamesLoading: boolean,
47
50
  labels: string[] | undefined,
48
51
  {
49
- urlParamKey = 'sum_by',
50
- withURLUpdate = true,
51
52
  defaultValue,
52
53
  }: {
53
- urlParamKey?: string;
54
- withURLUpdate?: boolean;
55
54
  defaultValue?: string[];
56
55
  } = {}
57
56
  ): [
58
- string[],
57
+ string[] | undefined,
59
58
  (labels: string[]) => void,
60
- {userSelectedSumBy: string[] | undefined; isLoading: boolean}
59
+ {
60
+ isLoading: boolean;
61
+ }
61
62
  ] => {
62
- const {navigateTo} = useParcaContext();
63
- const [userSelectedSumByParam, setUserSelectedSumByParam] = useURLState({
64
- param: urlParamKey,
65
- navigateTo,
66
- withURLUpdate,
67
- });
68
-
69
- const userSelectedSumBy = useMemo<string[] | undefined>(() => {
70
- if (userSelectedSumByParam?.length === 0) {
71
- return undefined;
72
- }
73
-
74
- if (userSelectedSumByParam === '__none__') {
75
- return [];
76
- }
63
+ const [userSelectedSumBy, setUserSelectedSumBy] = useState<Record<string, string[] | undefined>>(
64
+ profileType != null ? {[profileType.toString()]: defaultValue} : {}
65
+ );
77
66
 
78
- if (userSelectedSumByParam === undefined && defaultValue !== undefined) {
79
- return defaultValue;
80
- }
67
+ const setSumBy = useCallback(
68
+ (sumBy: string[]) => {
69
+ setUserSelectedSumBy(prev => {
70
+ if (profileType == null) {
71
+ return prev;
72
+ }
73
+
74
+ return {
75
+ ...prev,
76
+ [profileType.toString()]: sumBy,
77
+ };
78
+ });
79
+ },
80
+ [setUserSelectedSumBy, profileType]
81
+ );
81
82
 
82
- if (typeof userSelectedSumByParam === 'string') {
83
- return [userSelectedSumByParam];
84
- }
83
+ const {defaultSumBy} = useDefaultSumBy(profileType, labelNamesLoading, labels);
85
84
 
86
- return userSelectedSumByParam;
85
+ let sumBy =
86
+ userSelectedSumBy[profileType?.toString() ?? ''] ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
87
87
 
88
- // eslint-disable-next-line react-hooks/exhaustive-deps
89
- }, [userSelectedSumByParam]);
88
+ if (profileType?.delta !== true) {
89
+ sumBy = DEFAULT_EMPTY_SUM_BY;
90
+ }
90
91
 
91
- const setUserSelectedSumBy = useCallback(
92
- (sumBy: string[]) => {
93
- if (sumBy.length === 0) {
94
- setUserSelectedSumByParam('__none__');
95
- return;
96
- }
97
-
98
- if (sumBy.length === 1) {
99
- // Handle this separately to take care of the empty string scenario
100
- setUserSelectedSumByParam(sumBy[0]);
101
- return;
102
- }
103
-
104
- setUserSelectedSumByParam(sumBy);
92
+ return [
93
+ labelNamesLoading ? undefined : sumBy,
94
+ setSumBy,
95
+ {
96
+ isLoading: labelNamesLoading,
105
97
  },
106
- [setUserSelectedSumByParam]
107
- );
98
+ ];
99
+ };
108
100
 
101
+ export const useDefaultSumBy = (
102
+ profileType: ProfileType | undefined,
103
+ labelNamesLoading: boolean,
104
+ labels: string[] | undefined
105
+ ): {defaultSumBy: string[] | undefined; isLoading: boolean} => {
109
106
  const defaultSumBy = useMemo(() => {
110
107
  return getDefaultSumBy(profileType, labels);
111
108
  }, [profileType, labels]);
112
109
 
113
- let sumBy = userSelectedSumBy ?? defaultSumBy ?? DEFAULT_EMPTY_SUM_BY;
110
+ return {defaultSumBy, isLoading: labelNamesLoading};
111
+ };
114
112
 
115
- if (profileType?.delta !== true) {
116
- sumBy = DEFAULT_EMPTY_SUM_BY;
113
+ const getSumByFromParam = (param: string | string[] | undefined): string[] | undefined => {
114
+ if (param?.length === 0) {
115
+ return undefined;
116
+ }
117
+
118
+ if (param === '__none__') {
119
+ return [];
120
+ }
121
+
122
+ if (typeof param === 'string') {
123
+ return [param];
124
+ }
125
+
126
+ return param;
127
+ };
128
+
129
+ export const useSumByFromParams = (param: string | string[] | undefined): string[] | undefined => {
130
+ const sumBy = useMemo(() => {
131
+ return getSumByFromParam(param);
132
+ }, [param]);
133
+
134
+ return sumBy;
135
+ };
136
+
137
+ export const sumByToParam = (sumBy: string[] | undefined): string | string[] | undefined => {
138
+ if (sumBy === undefined) {
139
+ return undefined;
140
+ }
141
+
142
+ if (sumBy.length === 0) {
143
+ return '__none__';
117
144
  }
118
145
 
119
- return [sumBy, setUserSelectedSumBy, {userSelectedSumBy, isLoading: labelNamesLoading}];
146
+ return sumBy;
120
147
  };