@parca/profile 0.19.83 → 0.19.85

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 (83) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MatchersInput/index.d.ts +2 -34
  3. package/dist/MatchersInput/index.d.ts.map +1 -1
  4. package/dist/MatchersInput/index.js +14 -91
  5. package/dist/MetricsGraph/index.d.ts.map +1 -1
  6. package/dist/MetricsGraph/index.js +13 -1
  7. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  8. package/dist/ProfileMetricsGraph/index.js +6 -29
  9. package/dist/ProfileSelector/MetricsGraphSection.d.ts +8 -10
  10. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  11. package/dist/ProfileSelector/MetricsGraphSection.js +8 -41
  12. package/dist/ProfileSelector/index.d.ts +1 -29
  13. package/dist/ProfileSelector/index.d.ts.map +1 -1
  14. package/dist/ProfileSelector/index.js +14 -10
  15. package/dist/QueryControls/index.d.ts +46 -0
  16. package/dist/QueryControls/index.d.ts.map +1 -0
  17. package/dist/{ProfileSelector/QueryControls.js → QueryControls/index.js} +16 -13
  18. package/dist/SimpleMatchers/Select.js +1 -1
  19. package/dist/SimpleMatchers/index.d.ts +6 -10
  20. package/dist/SimpleMatchers/index.d.ts.map +1 -1
  21. package/dist/SimpleMatchers/index.js +30 -45
  22. package/dist/ViewMatchers/index.d.ts +0 -9
  23. package/dist/ViewMatchers/index.d.ts.map +1 -1
  24. package/dist/ViewMatchers/index.js +20 -12
  25. package/dist/contexts/LabelsQueryProvider.d.ts +35 -0
  26. package/dist/contexts/LabelsQueryProvider.d.ts.map +1 -0
  27. package/dist/contexts/LabelsQueryProvider.js +70 -0
  28. package/dist/contexts/UnifiedLabelsContext.d.ts +37 -0
  29. package/dist/contexts/UnifiedLabelsContext.d.ts.map +1 -0
  30. package/dist/contexts/UnifiedLabelsContext.js +88 -0
  31. package/dist/contexts/utils.d.ts +10 -0
  32. package/dist/contexts/utils.d.ts.map +1 -0
  33. package/dist/contexts/utils.js +31 -0
  34. package/dist/hooks/useLabels.d.ts +23 -0
  35. package/dist/hooks/useLabels.d.ts.map +1 -0
  36. package/dist/hooks/useLabels.js +75 -0
  37. package/dist/hooks/useQueryState.d.ts +3 -1
  38. package/dist/hooks/useQueryState.d.ts.map +1 -1
  39. package/dist/hooks/useQueryState.js +19 -12
  40. package/dist/index.d.ts +9 -3
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +9 -3
  43. package/dist/styles.css +1 -1
  44. package/dist/useSumBy.js +1 -1
  45. package/package.json +2 -2
  46. package/src/MatchersInput/index.tsx +17 -163
  47. package/src/MetricsGraph/index.tsx +17 -1
  48. package/src/ProfileMetricsGraph/index.tsx +17 -98
  49. package/src/ProfileSelector/MetricsGraphSection.tsx +25 -119
  50. package/src/ProfileSelector/index.tsx +115 -109
  51. package/src/{ProfileSelector/QueryControls.tsx → QueryControls/index.tsx} +76 -84
  52. package/src/SimpleMatchers/Select.tsx +1 -1
  53. package/src/SimpleMatchers/index.tsx +46 -84
  54. package/src/ViewMatchers/index.tsx +22 -30
  55. package/src/contexts/LabelsQueryProvider.tsx +142 -0
  56. package/src/contexts/UnifiedLabelsContext.tsx +155 -0
  57. package/src/contexts/utils.ts +43 -0
  58. package/src/hooks/useLabels.ts +121 -0
  59. package/src/hooks/useQueryState.ts +27 -16
  60. package/src/index.tsx +29 -3
  61. package/src/useSumBy.ts +1 -1
  62. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts +0 -29
  63. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts.map +0 -1
  64. package/dist/MetricsGraph/UtilizationMetrics/Throughput.js +0 -175
  65. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +0 -28
  66. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +0 -1
  67. package/dist/MetricsGraph/UtilizationMetrics/index.js +0 -186
  68. package/dist/ProfileSelector/QueryControls.d.ts +0 -43
  69. package/dist/ProfileSelector/QueryControls.d.ts.map +0 -1
  70. package/dist/contexts/MatchersInputLabelsContext.d.ts +0 -29
  71. package/dist/contexts/MatchersInputLabelsContext.d.ts.map +0 -1
  72. package/dist/contexts/MatchersInputLabelsContext.js +0 -79
  73. package/dist/contexts/SimpleMatchersLabelContext.d.ts +0 -25
  74. package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +0 -1
  75. package/dist/contexts/SimpleMatchersLabelContext.js +0 -115
  76. package/dist/contexts/UtilizationLabelsContext.d.ts +0 -15
  77. package/dist/contexts/UtilizationLabelsContext.d.ts.map +0 -1
  78. package/dist/contexts/UtilizationLabelsContext.js +0 -25
  79. package/src/MetricsGraph/UtilizationMetrics/Throughput.tsx +0 -405
  80. package/src/MetricsGraph/UtilizationMetrics/index.tsx +0 -426
  81. package/src/contexts/MatchersInputLabelsContext.tsx +0 -141
  82. package/src/contexts/SimpleMatchersLabelContext.tsx +0 -189
  83. package/src/contexts/UtilizationLabelsContext.tsx +0 -45
@@ -0,0 +1,46 @@
1
+ import { RpcError } from '@protobuf-ts/runtime-rpc';
2
+ import { type SelectInstance } from 'react-select';
3
+ import { ProfileTypesResponse, QueryServiceClient } from '@parca/client';
4
+ import { DateTimeRange } from '@parca/components';
5
+ import { ProfileType, Query } from '@parca/parser';
6
+ import { QuerySelection } from '../ProfileSelector';
7
+ interface QueryControlsProps {
8
+ queryClient: QueryServiceClient;
9
+ query: Query;
10
+ profileType: string | ProfileType;
11
+ timeRangeSelection: DateTimeRange;
12
+ setTimeRangeSelection: (range: DateTimeRange) => void;
13
+ setMatchersString: (matchers: string) => void;
14
+ setQueryExpression: (updateTs?: boolean) => void;
15
+ searchDisabled: boolean;
16
+ showProfileTypeSelector?: boolean;
17
+ showSumBySelector?: boolean;
18
+ showAdvancedMode?: boolean;
19
+ disableExplorativeQuerying?: boolean;
20
+ profileTypesData?: ProfileTypesResponse;
21
+ profileTypesLoading?: boolean;
22
+ selectedProfileName?: string;
23
+ setProfileName?: (name: string | undefined) => void;
24
+ profileTypesError?: RpcError;
25
+ viewComponent?: {
26
+ disableProfileTypesDropdown?: boolean;
27
+ disableExplorativeQuerying?: boolean;
28
+ labelnames?: string[];
29
+ createViewComponent?: React.ReactNode;
30
+ };
31
+ setQueryBrowserMode?: (mode: string) => void;
32
+ advancedModeForQueryBrowser?: boolean;
33
+ setAdvancedModeForQueryBrowser?: (mode: boolean) => void;
34
+ queryBrowserRef?: React.RefObject<HTMLDivElement>;
35
+ labels?: string[];
36
+ sumBySelection?: string[];
37
+ sumBySelectionLoading?: boolean;
38
+ setUserSumBySelection?: (sumBy: string[]) => void;
39
+ sumByRef?: React.RefObject<SelectInstance>;
40
+ draftSelection: QuerySelection;
41
+ setDraftMatchers: (selection: string) => void;
42
+ draftParsedQuery?: Query | null;
43
+ }
44
+ export declare function QueryControls({ profileType, timeRangeSelection, setTimeRangeSelection, setQueryExpression, searchDisabled, showProfileTypeSelector, showSumBySelector, showAdvancedMode, profileTypesData, profileTypesLoading, selectedProfileName, setProfileName, profileTypesError, viewComponent, setQueryBrowserMode, advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser, queryBrowserRef, labels, sumBySelection, sumBySelectionLoading, setUserSumBySelection, sumByRef, queryClient, draftSelection, setDraftMatchers, draftParsedQuery, }: QueryControlsProps): JSX.Element;
45
+ export {};
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/QueryControls/index.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAC,KAAK,cAAc,EAAC,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACvE,OAAO,EAAS,aAAa,EAAuC,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAC,WAAW,EAAE,KAAK,EAAC,MAAM,eAAe,CAAC;AAIjD,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAYlD,UAAU,kBAAkB;IAC1B,WAAW,EAAE,kBAAkB,CAAC;IAChC,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,WAAW,CAAC;IAClC,kBAAkB,EAAE,aAAa,CAAC;IAClC,qBAAqB,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACtD,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kBAAkB,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,OAAO,CAAC;IACxB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACpD,iBAAiB,CAAC,EAAE,QAAQ,CAAC;IAC7B,aAAa,CAAC,EAAE;QACd,2BAA2B,CAAC,EAAE,OAAO,CAAC;QACtC,0BAA0B,CAAC,EAAE,OAAO,CAAC;QACrC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;KACvC,CAAC;IACF,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,8BAA8B,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC3C,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,gBAAgB,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACjC;AAED,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,uBAA+B,EAC/B,iBAAyB,EACzB,gBAAuB,EACvB,gBAAgB,EAChB,mBAA2B,EAC3B,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,2BAAmC,EACnC,8BAA8B,EAC9B,eAAe,EACf,MAAW,EACX,cAAmB,EACnB,qBAA6B,EAC7B,qBAAqB,EACrB,QAAQ,EACR,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,EAAE,kBAAkB,GAAG,GAAG,CAAC,OAAO,CAsLlC"}
@@ -11,25 +11,29 @@ 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 { useState } from 'react';
14
+ import { useRef, useState } from 'react';
15
15
  import { Switch } from '@headlessui/react';
16
16
  import { Button, DateTimeRangePicker, useParcaContext } from '@parca/components';
17
17
  import { TEST_IDS, testId } from '@parca/test-utils';
18
18
  import MatchersInput from '../MatchersInput';
19
19
  import ProfileTypeSelector from '../ProfileTypeSelector';
20
- import SelectWithRefresh from '../SelectWithRefresh';
21
- import SimpleMatchers from '../SimpleMatchers';
20
+ import { SelectWithRefresh } from '../SelectWithRefresh';
21
+ import SimpleMatchers from '../SimpleMatchers/';
22
22
  import ViewMatchers from '../ViewMatchers';
23
- export function QueryControls({ showProfileTypeSelector, profileTypesData, profileTypesLoading, selectedProfileName, setProfileName, viewComponent, setQueryBrowserMode, advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser, setMatchersString, setQueryExpression, query, queryBrowserRef, timeRangeSelection, setTimeRangeSelection, searchDisabled, queryClient, labels, sumBySelection, sumBySelectionLoading, setUserSumBySelection, sumByRef, profileType, showSumBySelector, profileTypesError, refreshLabelNames, }) {
23
+ import { useLabelNames } from '../hooks/useLabels';
24
+ export function QueryControls({ profileType, timeRangeSelection, setTimeRangeSelection, setQueryExpression, searchDisabled, showProfileTypeSelector = false, showSumBySelector = false, showAdvancedMode = true, profileTypesData, profileTypesLoading = false, selectedProfileName, setProfileName, profileTypesError, viewComponent, setQueryBrowserMode, advancedModeForQueryBrowser = false, setAdvancedModeForQueryBrowser, queryBrowserRef, labels = [], sumBySelection = [], sumBySelectionLoading = false, setUserSumBySelection, sumByRef, queryClient, draftSelection, setDraftMatchers, draftParsedQuery, }) {
24
25
  const { timezone } = useParcaContext();
26
+ const defaultQueryBrowserRef = useRef(null);
27
+ const actualQueryBrowserRef = queryBrowserRef ?? defaultQueryBrowserRef;
25
28
  const [searchExecutedTimestamp, setSearchExecutedTimestamp] = useState(0);
26
- return (_jsxs("div", { className: "flex w-full flex-wrap items-start gap-2", ...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER), children: [showProfileTypeSelector && (_jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.PROFILE_TYPE_LABEL), children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: profileTypesError, disabled: viewComponent?.disableProfileTypesDropdown })] })), _jsxs("div", { className: "w-full flex-1 flex flex-col gap-1 mt-auto", ref: queryBrowserRef, ...testId(TEST_IDS.QUERY_BROWSER_CONTAINER), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.QUERY_LABEL), children: "Query" }), viewComponent?.disableExplorativeQuerying !== true && (_jsxs(_Fragment, { children: [_jsxs(Switch, { checked: advancedModeForQueryBrowser, onChange: () => {
27
- setAdvancedModeForQueryBrowser(!advancedModeForQueryBrowser);
28
- setQueryBrowserMode(advancedModeForQueryBrowser ? 'simple' : 'advanced');
29
+ const { refetch: refetchLabelNames } = useLabelNames(queryClient, profileType, timeRangeSelection.getFromMs(), timeRangeSelection.getToMs());
30
+ return (_jsxs("div", { className: "flex w-full flex-wrap items-start gap-2", ...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER), children: [showProfileTypeSelector && (_jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.PROFILE_TYPE_LABEL), children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName ?? (() => { }), error: profileTypesError, disabled: viewComponent?.disableProfileTypesDropdown })] })), _jsxs("div", { className: "w-full flex-1 flex flex-col gap-1 mt-auto", ref: actualQueryBrowserRef, ...testId(TEST_IDS.QUERY_BROWSER_CONTAINER), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.QUERY_LABEL), children: "Query" }), showAdvancedMode && viewComponent?.disableExplorativeQuerying !== true && (_jsxs(_Fragment, { children: [_jsxs(Switch, { checked: advancedModeForQueryBrowser, onChange: () => {
31
+ setAdvancedModeForQueryBrowser?.(!advancedModeForQueryBrowser);
32
+ setQueryBrowserMode?.(advancedModeForQueryBrowser ? 'simple' : 'advanced');
29
33
  }, className: `${advancedModeForQueryBrowser ? 'bg-indigo-600' : 'bg-gray-400 dark:bg-gray-800'} relative inline-flex h-[20px] w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`, ...testId(TEST_IDS.ADVANCED_MODE_SWITCH), children: [_jsx("span", { className: "sr-only", children: "Use setting" }), _jsx("span", { "aria-hidden": "true", className: `${advancedModeForQueryBrowser ? 'translate-x-6' : 'translate-x-0'} pointer-events-none inline-block h-[16px] w-[16px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out` })] }), _jsx("label", { className: "text-xs", ...testId(TEST_IDS.QUERY_MODE_LABEL), children: "Advanced Mode" })] }))] }), viewComponent?.createViewComponent] }), viewComponent?.disableExplorativeQuerying === true &&
30
34
  viewComponent?.labelnames !== undefined &&
31
- viewComponent?.labelnames.length >= 1 ? (_jsx(ViewMatchers, { labelNames: viewComponent.labelnames, setMatchersString: setMatchersString, profileType: selectedProfileName, runQuery: setQueryExpression, currentQuery: query, queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs() })) : advancedModeForQueryBrowser ? (_jsx(MatchersInput, { setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName, queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs() })) : (_jsx(SimpleMatchers, { setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName, queryBrowserRef: queryBrowserRef, queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs(), searchExecutedTimestamp: searchExecutedTimestamp }))] }), showSumBySelector && (_jsxs("div", { ...testId(TEST_IDS.SUM_BY_CONTAINER), children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", ...testId(TEST_IDS.SUM_BY_LABEL), children: "Sum by" }) }), _jsx(SelectWithRefresh, { id: "h-sum-by-selector", "data-testid": testId(TEST_IDS.SUM_BY_SELECT)['data-testid'], defaultValue: [], isMulti: true, isClearable: false, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-full max-w-80", classNamePrefix: "parca-select", value: (sumBySelection ?? []).map(sumBy => ({ label: sumBy, value: sumBy })), onChange: newValue => {
32
- setUserSumBySelection(newValue.map(option => option.value));
35
+ viewComponent?.labelnames.length >= 1 ? (_jsx(ViewMatchers, { labelNames: viewComponent.labelnames })) : showAdvancedMode && advancedModeForQueryBrowser ? (_jsx(MatchersInput, {})) : (_jsx(SimpleMatchers, { queryBrowserRef: actualQueryBrowserRef, searchExecutedTimestamp: searchExecutedTimestamp, draftSelection: draftSelection, setDraftMatchers: setDraftMatchers, draftParsedQuery: draftParsedQuery }))] }), showSumBySelector && (_jsxs("div", { ...testId(TEST_IDS.SUM_BY_CONTAINER), children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", ...testId(TEST_IDS.SUM_BY_LABEL), children: "Sum by" }) }), _jsx(SelectWithRefresh, { id: "h-sum-by-selector", "data-testid": testId(TEST_IDS.SUM_BY_SELECT)['data-testid'], defaultValue: [], isMulti: true, isClearable: false, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-full max-w-80", classNamePrefix: "parca-select", value: sumBySelection.map(sumBy => ({ label: sumBy, value: sumBy })), onChange: newValue => {
36
+ setUserSumBySelection?.(newValue.map(option => option.value));
33
37
  }, placeholder: "Labels...", styles: {
34
38
  indicatorSeparator: () => ({ display: 'none' }),
35
39
  menu: provided => ({
@@ -41,11 +45,10 @@ export function QueryControls({ showProfileTypeSelector, profileTypesData, profi
41
45
  minWidth: '320px',
42
46
  position: 'absolute',
43
47
  }),
44
- // menu: provided => ({...provided, width: 'max-content', zIndex: 50}), // Setting the same zIndex as drop down menus
45
- }, isLoading: sumBySelectionLoading, isDisabled: !profileType.delta,
48
+ }, isLoading: sumBySelectionLoading, isDisabled: !profileType?.delta,
46
49
  // @ts-expect-error
47
50
  ref: sumByRef, onKeyDown: e => {
48
- const currentRef = sumByRef.current;
51
+ const currentRef = sumByRef?.current;
49
52
  if (currentRef == null) {
50
53
  return;
51
54
  }
@@ -60,7 +63,7 @@ export function QueryControls({ showProfileTypeSelector, profileTypesData, profi
60
63
  setQueryExpression(true);
61
64
  currentRef.blur();
62
65
  }
63
- }, onRefresh: refreshLabelNames, refreshTitle: "Refresh label names", refreshTestId: "sum-by-refresh-button", menuTestId: TEST_IDS.SUM_BY_SELECT_FLYOUT })] })), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection, timezone: timezone, ...testId(TEST_IDS.DATE_TIME_RANGE_PICKER) }), _jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.SEARCH_BUTTON_LABEL), children: "\u00A0" }), _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
66
+ }, onRefresh: refetchLabelNames, refreshTitle: "Refresh label names", refreshTestId: "sum-by-refresh-button", menuTestId: TEST_IDS.SUM_BY_SELECT_FLYOUT })] })), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection, timezone: timezone, ...testId(TEST_IDS.DATE_TIME_RANGE_PICKER) }), _jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.SEARCH_BUTTON_LABEL), children: "\u00A0" }), _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
64
67
  e.preventDefault();
65
68
  setSearchExecutedTimestamp(Date.now());
66
69
  setQueryExpression(true);
@@ -179,7 +179,7 @@ const CustomSelect = ({ items: itemsProp, selectedKey, onSelection, placeholder
179
179
  setIsOpen(false);
180
180
  }, children: "Add" }) }))] })) : (_jsx("input", { ref: searchInputRef, type: "text", className: "w-full px-4 h-[45px] text-sm border-none rounded-none ring-0 outline-none bg-gray-50 dark:bg-gray-800 dark:text-white", placeholder: "Search...", value: searchTerm, onChange: e => setSearchTerm(e.target.value) })) }) })), _jsx("div", { className: "flex-1 min-h-0", children: loading === true ? (_jsx("div", { className: "w-[270px]", children: loader })) : groupedFilteredItems.length === 0 ? (_jsx("div", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center", ...testId(TEST_IDS.LABEL_VALUE_NO_RESULTS), children: "No values found" })) : (groupedFilteredItems.map(group => (_jsxs(_Fragment, { children: [groupedFilteredItems.length > 1 &&
181
181
  groupedFilteredItems.every(g => g.type !== '') &&
182
- group.type !== '' ? (_jsx("div", { className: "pl-2", children: _jsx(DividerWithLabel, { label: group.type }) })) : null, group.values.map((item, index) => (_jsx(OptionItem, { item: item, index: index, optionRefs: optionRefs, focusedIndex: focusedIndex, selectedKey: selectedKey, handleSelection: handleSelection }, item.key)))] })))) }), refetchValues !== undefined && loading !== true && (_jsx(RefreshButton, { onClick: () => void handleRefetch(), disabled: isRefetching, title: "Refresh label values", testId: TEST_IDS.LABEL_VALUE_REFRESH_BUTTON, sticky: true, loading: isRefetching }))] }) }))] }));
182
+ group.type !== '' ? (_jsx("div", { className: "pl-2", children: _jsx(DividerWithLabel, { label: group.type }) })) : null, group.values.map((item, index) => (_jsx(OptionItem, { item: item, index: index, optionRefs: optionRefs, focusedIndex: focusedIndex, selectedKey: selectedKey, handleSelection: handleSelection }, item.key)))] })))) }), refetchValues !== undefined && loading !== true && (_jsx(RefreshButton, { onClick: () => void handleRefetch(), disabled: isRefetching, title: "Refresh results", testId: TEST_IDS.LABEL_VALUE_REFRESH_BUTTON, sticky: true, loading: isRefetching }))] }) }))] }));
183
183
  };
184
184
  const OptionItem = ({ item, optionRefs, index, focusedIndex, selectedKey, handleSelection, }) => {
185
185
  return (_jsxs("div", { ref: el => {
@@ -1,18 +1,14 @@
1
- import { QueryServiceClient } from '@parca/client';
2
1
  import { Query } from '@parca/parser';
2
+ import { QuerySelection } from '../ProfileSelector';
3
3
  import { type SelectItem } from './Select';
4
4
  interface Props {
5
- queryClient: QueryServiceClient;
6
- setMatchersString: (arg: string) => void;
7
- runQuery: () => void;
8
- currentQuery: Query;
9
- profileType: string;
10
5
  queryBrowserRef: React.RefObject<HTMLDivElement>;
11
- start?: number;
12
- end?: number;
13
6
  searchExecutedTimestamp?: number;
7
+ draftSelection: QuerySelection;
8
+ setDraftMatchers: (selection: string) => void;
9
+ draftParsedQuery?: Query | null;
14
10
  }
15
11
  export declare const transformLabelsForSelect: (labelNames: string[]) => SelectItem[];
16
- export default function SimpleMathersWithProvider(props: Props): JSX.Element;
17
- export {};
12
+ declare const SimpleMatchers: ({ queryBrowserRef, searchExecutedTimestamp, draftSelection, setDraftMatchers, draftParsedQuery, }: Props) => JSX.Element;
13
+ export default SimpleMatchers;
18
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,OAAe,EAAC,KAAK,UAAU,EAAC,MAAM,UAAU,CAAC;AAEjD,UAAU,KAAK;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAoBD,eAAO,MAAM,wBAAwB,GAAI,YAAY,MAAM,EAAE,KAAG,UAAU,EAQzE,CAAC;AAidF,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,KAAK,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAoB3E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAIpC,OAAO,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAGlD,OAAe,EAAC,KAAK,UAAU,EAAC,MAAM,UAAU,CAAC;AAEjD,UAAU,KAAK;IACb,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,gBAAgB,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACjC;AAUD,eAAO,MAAM,wBAAwB,GAAI,YAAY,MAAM,EAAE,KAAG,UAAU,EAQzE,CAAC;AAiDF,QAAA,MAAM,cAAc,GAAI,mGAMrB,KAAK,KAAG,GAAG,CAAC,OAoZd,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -11,31 +11,22 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } 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 { useCallback, useEffect, useMemo, useState } from 'react';
14
+ import { useCallback, useEffect, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import { useQueryClient } from '@tanstack/react-query';
17
17
  import cx from 'classnames';
18
- import { useGrpcMetadata } from '@parca/components';
18
+ import { useGrpcMetadata, useParcaContext } from '@parca/components';
19
19
  import { TEST_IDS, testId } from '@parca/test-utils';
20
20
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
21
- import { LabelProvider, useLabels } from '../contexts/SimpleMatchersLabelContext';
22
- import { useUtilizationLabels } from '../contexts/UtilizationLabelsContext';
21
+ import { useUnifiedLabels } from '../contexts/UnifiedLabelsContext';
22
+ import { transformLabelName } from '../contexts/utils';
23
23
  import Select from './Select';
24
- const trimOtelPrefix = (labelName) => {
25
- if (labelName.startsWith('attributes_resource.')) {
26
- return labelName.replace('attributes_resource.', '');
27
- }
28
- if (labelName.startsWith('attributes.')) {
29
- return labelName.replace('attributes.', '');
30
- }
31
- return labelName;
32
- };
33
24
  export const transformLabelsForSelect = (labelNames) => {
34
25
  return labelNames.map(labelName => ({
35
26
  key: labelName,
36
27
  element: {
37
- active: _jsx(_Fragment, { children: trimOtelPrefix(labelName) }),
38
- expanded: _jsx(_Fragment, { children: trimOtelPrefix(labelName) }),
28
+ active: _jsx(_Fragment, { children: transformLabelName(labelName) }),
29
+ expanded: _jsx(_Fragment, { children: transformLabelName(labelName) }),
39
30
  },
40
31
  }));
41
32
  };
@@ -69,15 +60,16 @@ const operatorOptions = [
69
60
  },
70
61
  },
71
62
  ];
72
- const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileType, queryBrowserRef, start, end, searchExecutedTimestamp, }) => {
73
- const utilizationLabels = useUtilizationLabels();
63
+ const SimpleMatchers = ({ queryBrowserRef, searchExecutedTimestamp, draftSelection, setDraftMatchers, draftParsedQuery, }) => {
74
64
  const [queryRows, setQueryRows] = useState([
75
65
  { labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false },
76
66
  ]);
77
67
  const reactQueryClient = useQueryClient();
78
68
  const metadata = useGrpcMetadata();
69
+ const { queryServiceClient: parcaQueryClient } = useParcaContext();
79
70
  const [showAll, setShowAll] = useState(false);
80
71
  const [isActivelyEditing, setIsActivelyEditing] = useState(false);
72
+ const { labelNameMappingsForSimpleMatchers: labelNameOptions, isLabelNamesLoading: labelNamesLoading, refetchLabelNames, } = useUnifiedLabels();
81
73
  // Reset editing mode when search is executed
82
74
  useEffect(() => {
83
75
  if (searchExecutedTimestamp !== undefined && searchExecutedTimestamp > 0) {
@@ -88,14 +80,20 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
88
80
  const visibleRows = showAll || isActivelyEditing ? queryRows : queryRows.slice(0, 3);
89
81
  const hiddenRowsCount = queryRows.length - 3;
90
82
  const maxWidthInPixels = `max-w-[${queryBrowserRef.current?.offsetWidth.toString()}px]`;
91
- const currentMatchers = currentQuery.matchersString();
83
+ const currentMatchers = draftParsedQuery?.matchersString();
84
+ const profileType = draftParsedQuery?.profileType().toString();
85
+ const start = draftSelection.from;
86
+ const end = draftSelection.to;
92
87
  const fetchLabelValues = useCallback(async (labelName) => {
93
- if (labelName == null || labelName === '' || profileType == null || profileType === '') {
88
+ if (labelName == null || labelName === '') {
89
+ return [];
90
+ }
91
+ if (profileType == null || profileType === '') {
94
92
  return [];
95
93
  }
96
94
  try {
97
95
  const values = await reactQueryClient.fetchQuery([labelName, profileType, start, end], async () => {
98
- const response = await queryClient.values({
96
+ const response = await parcaQueryClient.values({
99
97
  labelName,
100
98
  match: [],
101
99
  profileType,
@@ -116,18 +114,14 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
116
114
  console.error('Error fetching label values:', error);
117
115
  return [];
118
116
  }
119
- }, [queryClient, metadata, profileType, reactQueryClient, start, end]);
120
- const fetchLabelValuesUtilization = useCallback(async (labelName) => {
121
- return (await utilizationLabels?.utilizationFetchLabelValues?.(labelName)) ?? [];
122
- }, [utilizationLabels]);
117
+ }, [parcaQueryClient, metadata, profileType, reactQueryClient, start, end]);
123
118
  const updateMatchersString = useCallback((rows) => {
124
119
  const matcherString = rows
125
120
  .filter(row => row.labelName.length > 0 && row.labelValue)
126
121
  .map(row => `${row.labelName}${row.operator}"${row.labelValue}"`)
127
122
  .join(',');
128
- setMatchersString(matcherString);
129
- }, [setMatchersString]);
130
- const { labelNameOptions, isLoading: labelNamesLoading, refetchLabelValues, refetchLabelNames, } = useLabels();
123
+ setDraftMatchers(matcherString);
124
+ }, [setDraftMatchers]);
131
125
  // Helper to ensure selected label name is in the options (for page load before API returns)
132
126
  const getLabelNameOptionsWithSelected = useCallback((selectedLabelName) => {
133
127
  if (selectedLabelName === '')
@@ -184,14 +178,10 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
184
178
  }
185
179
  }, [labelNameOptions]);
186
180
  const fetchLabelValuesUnified = useCallback(async (labelName) => {
187
- const labelType = labelNameOptions.find(option => option.values.some(e => e.key === labelName))?.type;
188
- const labelValues = labelType === 'gpu'
189
- ? await fetchLabelValuesUtilization(labelName)
190
- : await fetchLabelValues(labelName);
191
- return labelValues;
192
- }, [fetchLabelValues, fetchLabelValuesUtilization, labelNameOptions]);
181
+ return await fetchLabelValues(labelName);
182
+ }, [fetchLabelValues]);
193
183
  useEffect(() => {
194
- if (currentMatchers === '') {
184
+ if (currentMatchers === '' || currentMatchers === undefined) {
195
185
  const defaultRow = {
196
186
  labelName: '',
197
187
  operator: '=',
@@ -205,7 +195,7 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
205
195
  let isCancelled = false;
206
196
  const fetchAndSetQueryRows = async () => {
207
197
  const newRows = await Promise.all(currentMatchers.split(',').map(async (matcher) => {
208
- const match = matcher.match(/([^=!~]+)([=!~]{1,2})(.+)/);
198
+ const match = matcher.match(/^([^=!~]+)([=!~]{1,2})(.+)$/);
209
199
  if (match === null)
210
200
  return null;
211
201
  const [, labelName, operator, labelValue] = match;
@@ -314,11 +304,14 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
314
304
  };
315
305
  }, [queryRows, fetchLabelValuesUnified]);
316
306
  const isRowRegex = (row) => row.operator === '=~' || row.operator === '!~';
307
+ const handleRefetchForLabelValues = useCallback(async (labelName) => {
308
+ await fetchLabelValuesUnified(labelName);
309
+ }, [fetchLabelValuesUnified]);
317
310
  return (_jsxs("div", { className: `flex items-center gap-3 ${maxWidthInPixels} w-full flex-wrap`, id: "simple-matchers", ...testId(TEST_IDS.SIMPLE_MATCHERS_CONTAINER), children: [visibleRows.map((row, index) => (_jsxs("div", { className: "flex items-center", ...testId(TEST_IDS.SIMPLE_MATCHER_ROW), children: [_jsx(Select, { items: getLabelNameOptionsWithSelected(row.labelName), onSelection: value => handleUpdateRow(index, 'labelName', value), placeholder: "Select label name", selectedKey: row.labelName, className: "rounded-tr-none rounded-br-none ring-0 focus:ring-0 outline-none", loading: labelNamesLoading, searchable: true, ...testId(TEST_IDS.LABEL_NAME_SELECT), refetchValues: refetchLabelNames }), _jsx(Select, { items: operatorOptions, onSelection: value => handleUpdateRow(index, 'operator', value), selectedKey: row.operator, className: "rounded-none ring-0 focus:ring-0 outline-none", ...testId(TEST_IDS.OPERATOR_SELECT) }), _jsx(Select, { items: transformLabelsForSelect(row.labelValue !== '' && !row.labelValues.includes(row.labelValue)
318
311
  ? [...row.labelValues, row.labelValue]
319
312
  : row.labelValues), onSelection: value => handleUpdateRow(index, 'labelValue', value), placeholder: "Select label value", selectedKey: row.labelValue, className: "rounded-none ring-0 focus:ring-0 outline-none max-w-48", optionsClassname: cx('max-w-[600px]', {
320
313
  'w-[300px]': isRowRegex(row),
321
- }), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row), ...testId(TEST_IDS.LABEL_VALUE_SELECT), refetchValues: async () => await refetchLabelValues(row.labelName), showLoadingInButton: true }), _jsx("button", { onClick: () => removeRow(index), className: cx('p-2 border-gray-200 border rounded rounded-tl-none rounded-bl-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900'), ...testId(TEST_IDS.REMOVE_MATCHER_BUTTON), children: _jsx(Icon, { icon: "carbon:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }, index))), queryRows.length > 3 && !isActivelyEditing && (_jsx("button", { onClick: () => {
314
+ }), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row), ...testId(TEST_IDS.LABEL_VALUE_SELECT), refetchValues: async () => await handleRefetchForLabelValues(row.labelName), showLoadingInButton: true }), _jsx("button", { onClick: () => removeRow(index), className: cx('p-2 border-gray-200 border rounded rounded-tl-none rounded-bl-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900'), ...testId(TEST_IDS.REMOVE_MATCHER_BUTTON), children: _jsx(Icon, { icon: "carbon:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }, index))), queryRows.length > 3 && !isActivelyEditing && (_jsx("button", { onClick: () => {
322
315
  if (showAll) {
323
316
  // Clicking "Show less" - collapse and stop editing mode
324
317
  setShowAll(false);
@@ -330,12 +323,4 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
330
323
  }
331
324
  }, className: "mr-2 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900", ...testId(showAll ? TEST_IDS.SHOW_LESS_BUTTON : TEST_IDS.SHOW_MORE_BUTTON), children: showAll ? 'Show less' : `Show ${hiddenRowsCount} more` })), _jsx("button", { onClick: addNewRow, className: "p-2 border-gray-200 dark:bg-gray-900 dark:border-gray-600 border rounded focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", ...testId(TEST_IDS.ADD_MATCHER_BUTTON), children: _jsx(Icon, { icon: "material-symbols:add", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }));
332
325
  };
333
- export default function SimpleMathersWithProvider(props) {
334
- const labelNameFromMatchers = useMemo(() => {
335
- if (props.currentQuery === undefined)
336
- return [];
337
- const matchers = props.currentQuery.matchers;
338
- return matchers.map(matcher => matcher.key);
339
- }, [props.currentQuery]);
340
- return (_jsx(LabelProvider, { queryClient: props.queryClient, profileType: props.profileType, labelNameFromMatchers: labelNameFromMatchers, start: props.start, end: props.end, children: _jsx(SimpleMatchers, { ...props }) }));
341
- }
326
+ export default SimpleMatchers;
@@ -1,15 +1,6 @@
1
1
  import React from 'react';
2
- import { QueryServiceClient } from '@parca/client';
3
- import { Query } from '@parca/parser';
4
2
  interface Props {
5
3
  labelNames: string[];
6
- profileType: string;
7
- runQuery: () => void;
8
- currentQuery: Query;
9
- queryClient: QueryServiceClient;
10
- setMatchersString: (arg: string) => void;
11
- start?: number;
12
- end?: number;
13
4
  }
14
5
  declare const ViewMatchers: React.FC<Props>;
15
6
  export default ViewMatchers;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ViewMatchers/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAKtE,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAyKjC,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ViewMatchers/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAatE,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAwKjC,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -14,15 +14,23 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
14
14
  import { useCallback, useEffect, useRef, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
- import { useGrpcMetadata } from '@parca/components';
17
+ import { useGrpcMetadata, useParcaContext } from '@parca/components';
18
18
  import { TEST_IDS, testId } from '@parca/test-utils';
19
19
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
20
20
  import CustomSelect from '../SimpleMatchers/Select';
21
- const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatchersString, start, end, currentQuery, }) => {
21
+ import { useUnifiedLabels } from '../contexts/UnifiedLabelsContext';
22
+ import { useQueryState } from '../hooks/useQueryState';
23
+ const ViewMatchers = ({ labelNames }) => {
22
24
  const [labelValuesMap, setLabelValuesMap] = useState({});
23
25
  const [isLoading, setIsLoading] = useState({});
24
26
  const metadata = useGrpcMetadata();
25
- const currentMatchers = currentQuery.matchersString();
27
+ const { queryServiceClient: parcaQueryClient } = useParcaContext();
28
+ const { suffix } = useUnifiedLabels();
29
+ const { draftSelection, setDraftMatchers, commitDraft, draftParsedQuery } = useQueryState({ suffix });
30
+ const currentMatchers = draftParsedQuery?.matchersString();
31
+ const profileType = draftParsedQuery?.profileType().toString();
32
+ const start = draftSelection.from;
33
+ const end = draftSelection.to;
26
34
  const parseCurrentMatchers = useCallback((matchersString) => {
27
35
  const matches = matchersString.match(/(\w+)="([^"]+)"/g);
28
36
  if (matches === null)
@@ -37,19 +45,19 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
37
45
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38
46
  {});
39
47
  }, []);
40
- const initialSelections = parseCurrentMatchers(currentMatchers);
48
+ const initialSelections = parseCurrentMatchers(currentMatchers ?? '');
41
49
  const selectionsRef = useRef(initialSelections);
42
- const runQueryRef = useRef(runQuery);
50
+ const commitDraftRef = useRef(commitDraft);
43
51
  const timeoutRef = useRef(null);
44
52
  useEffect(() => {
45
- runQueryRef.current = runQuery;
46
- }, [runQuery]);
53
+ commitDraftRef.current = commitDraft;
54
+ }, [commitDraft]);
47
55
  useEffect(() => {
48
56
  selectionsRef.current = initialSelections;
49
57
  }, [initialSelections]);
50
58
  const fetchLabelValues = useCallback(async (labelName) => {
51
59
  try {
52
- const response = await queryClient.values({
60
+ const response = await parcaQueryClient.values({
53
61
  labelName,
54
62
  match: [],
55
63
  profileType,
@@ -66,7 +74,7 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
66
74
  console.error('Error fetching label values:', error);
67
75
  return [];
68
76
  }
69
- }, [queryClient, metadata, profileType, start, end]);
77
+ }, [parcaQueryClient, metadata, profileType, start, end]);
70
78
  const fetchAllLabelValues = useCallback(async () => {
71
79
  const newLabelValuesMap = {};
72
80
  const newIsLoading = {};
@@ -88,14 +96,14 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
88
96
  .filter(([_, v]) => v !== null && v !== '')
89
97
  .map(([ln, v]) => `${ln}="${v}"`);
90
98
  const matcherString = matcherParts.join(',');
91
- setMatchersString(matcherString);
99
+ setDraftMatchers(matcherString);
92
100
  if (timeoutRef.current !== null) {
93
101
  clearTimeout(timeoutRef.current);
94
102
  }
95
103
  timeoutRef.current = setTimeout(() => {
96
- runQueryRef.current();
104
+ commitDraftRef.current();
97
105
  }, 300);
98
- }, [setMatchersString]);
106
+ }, [setDraftMatchers]);
99
107
  const handleSelection = useCallback((labelName, value) => {
100
108
  selectionsRef.current = {
101
109
  ...selectionsRef.current,
@@ -0,0 +1,35 @@
1
+ import { QueryServiceClient } from '@parca/client';
2
+ import { Query } from '@parca/parser';
3
+ export interface LabelsQueryProviderContextType {
4
+ isLabelNamesLoading: boolean;
5
+ isLabelValuesLoading: boolean;
6
+ currentLabelName: string | null;
7
+ setCurrentLabelName: (name: string | null) => void;
8
+ refetchLabelValues: () => Promise<void>;
9
+ refetchLabelNames: () => Promise<void>;
10
+ labelNames: string[];
11
+ labelValues: string[];
12
+ queryClient: QueryServiceClient;
13
+ setMatchersString: (arg: string) => void;
14
+ runQuery: () => void;
15
+ currentQuery: Query;
16
+ profileType: string;
17
+ start?: number;
18
+ end?: number;
19
+ suffix?: '_a' | '_b';
20
+ }
21
+ interface LabelsQueryProviderProps {
22
+ children: React.ReactNode;
23
+ queryClient: QueryServiceClient;
24
+ setMatchersString: (arg: string) => void;
25
+ runQuery: () => void;
26
+ currentQuery: Query;
27
+ profileType: string;
28
+ start?: number;
29
+ end?: number;
30
+ suffix?: '_a' | '_b';
31
+ }
32
+ export declare function LabelsQueryProvider({ children, queryClient, setMatchersString, runQuery, currentQuery, profileType, start, end, suffix, }: LabelsQueryProviderProps): JSX.Element;
33
+ export declare function useLabelsQueryProvider(): LabelsQueryProviderContextType;
34
+ export {};
35
+ //# sourceMappingURL=LabelsQueryProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LabelsQueryProvider.d.ts","sourceRoot":"","sources":["../../src/contexts/LabelsQueryProvider.tsx"],"names":[],"mappings":"AAoCA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAKpC,MAAM,WAAW,8BAA8B;IAC7C,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAID,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,KAAK,EACL,GAAG,EACH,MAAM,GACP,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CA2CxC;AAED,wBAAgB,sBAAsB,IAAI,8BAA8B,CAMvE"}
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ /**
15
+ * LabelsQueryProvider - Data Fetching Layer
16
+ *
17
+ * This context provider is responsible for fetching label data from the Parca API
18
+ * and making it available to child components through React Context.
19
+ *
20
+ * Purpose:
21
+ * - Fetches label names and values from the Parca profiling API
22
+ * - Manages loading states for label data
23
+ * - Provides refetch functions for manual data refresh
24
+ * - Acts as the primary data source in the label provider architecture
25
+ *
26
+ * Architecture Pattern:
27
+ * This is the first layer in a three-layer architecture:
28
+ * 1. LabelsQueryProvider (this file) - Fetches data from API
29
+ * 2. LabelsSource (in ProfileSelector) - Transforms/merges data
30
+ * 3. UnifiedLabelsProvider - Provides unified interface to UI components
31
+ *
32
+ * Consumer Hook: useLabelsQueryProvider()
33
+ */
34
+ import { createContext, useContext, useState } from 'react';
35
+ import { useLabelNames, useLabelValues } from '../hooks/useLabels';
36
+ import { useExtractedLabelNames } from './utils';
37
+ const LabelsQueryProviderContext = createContext(null);
38
+ export function LabelsQueryProvider({ children, queryClient, setMatchersString, runQuery, currentQuery, profileType, start, end, suffix, }) {
39
+ const [currentLabelName, setCurrentLabelName] = useState(null);
40
+ const { result: labelNamesResponse, loading: isLabelNamesLoading, refetch: labelNamesRefetch, } = useLabelNames(queryClient, profileType, start, end);
41
+ const labelNames = useExtractedLabelNames(labelNamesResponse.response, labelNamesResponse.error);
42
+ const { result: labelValuesOriginal, loading: isLabelValuesLoading, refetch: labelValuesRefetch, } = useLabelValues(queryClient, currentLabelName ?? '', profileType, start, end);
43
+ const labelValues = labelValuesOriginal.response;
44
+ const value = {
45
+ labelNames,
46
+ labelValues,
47
+ isLabelNamesLoading,
48
+ isLabelValuesLoading,
49
+ refetchLabelValues: labelValuesRefetch,
50
+ refetchLabelNames: labelNamesRefetch,
51
+ queryClient,
52
+ setMatchersString,
53
+ runQuery,
54
+ currentQuery,
55
+ profileType,
56
+ start,
57
+ end,
58
+ setCurrentLabelName,
59
+ currentLabelName,
60
+ suffix,
61
+ };
62
+ return (_jsx(LabelsQueryProviderContext.Provider, { value: value, children: children }));
63
+ }
64
+ export function useLabelsQueryProvider() {
65
+ const context = useContext(LabelsQueryProviderContext);
66
+ if (context === null) {
67
+ throw new Error('useLabelsQueryProvider must be used within a LabelsQueryProvider');
68
+ }
69
+ return context;
70
+ }
@@ -0,0 +1,37 @@
1
+ import type { SelectItem } from '../SimpleMatchers/Select';
2
+ import { type LabelNameMapping } from './utils';
3
+ interface LabelNameSection {
4
+ type: string;
5
+ values: SelectItem[];
6
+ }
7
+ interface UnifiedLabelsContextType {
8
+ labelNameMappingsForMatchersInput: LabelNameMapping[];
9
+ labelNameMappingsForSimpleMatchers: LabelNameSection[];
10
+ labelNames: string[];
11
+ labelValues: string[];
12
+ isLabelNamesLoading: boolean;
13
+ isLabelValuesLoading: boolean;
14
+ currentLabelName: string | null;
15
+ setCurrentLabelName: (name: string | null) => void;
16
+ shouldHandlePrefixes: boolean;
17
+ refetchLabelValues: () => Promise<void>;
18
+ refetchLabelNames: () => Promise<void>;
19
+ labelNameFromMatchers: string[];
20
+ suffix?: '_a' | '_b';
21
+ }
22
+ interface UnifiedLabelsProviderProps {
23
+ children: React.ReactNode;
24
+ currentLabelName: string | null;
25
+ setCurrentLabelName: (name: string | null) => void;
26
+ labelNames: string[];
27
+ labelValues: string[];
28
+ isLabelNamesLoading: boolean;
29
+ isLabelValuesLoading: boolean;
30
+ refetchLabelValues: () => Promise<void>;
31
+ refetchLabelNames: () => Promise<void>;
32
+ suffix?: '_a' | '_b';
33
+ }
34
+ export declare function UnifiedLabelsProvider({ children, labelNames, isLabelNamesLoading, isLabelValuesLoading, refetchLabelValues, refetchLabelNames, currentLabelName, setCurrentLabelName, labelValues, suffix, }: UnifiedLabelsProviderProps): JSX.Element;
35
+ export declare function useUnifiedLabels(): UnifiedLabelsContextType;
36
+ export {};
37
+ //# sourceMappingURL=UnifiedLabelsContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnifiedLabelsContext.d.ts","sourceRoot":"","sources":["../../src/contexts/UnifiedLabelsContext.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAuB,KAAK,gBAAgB,EAAC,MAAM,SAAS,CAAC;AAEpE,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,UAAU,wBAAwB;IAChC,iCAAiC,EAAE,gBAAgB,EAAE,CAAC;IACtD,kCAAkC,EAAE,gBAAgB,EAAE,CAAC;IACvD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAID,UAAU,0BAA0B;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAEnD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAE9B,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,MAAM,GACP,EAAE,0BAA0B,GAAG,GAAG,CAAC,OAAO,CAqD1C;AAED,wBAAgB,gBAAgB,IAAI,wBAAwB,CAM3D"}