@parca/profile 0.19.53 → 0.19.55

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 (36) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/MatchersInput/index.js +3 -3
  3. package/dist/MetricsGraph/MetricsContextMenu/index.js +2 -2
  4. package/dist/MetricsGraph/MetricsTooltip/index.js +2 -2
  5. package/dist/MetricsGraph/index.js +2 -2
  6. package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
  7. package/dist/ProfileExplorer/ProfileExplorerCompare.js +2 -2
  8. package/dist/ProfileFlameGraph/index.js +2 -2
  9. package/dist/ProfileMetricsGraph/index.js +3 -3
  10. package/dist/ProfileSelector/QueryControls.js +6 -6
  11. package/dist/ProfileSelector/index.d.ts.map +1 -1
  12. package/dist/ProfileSelector/index.js +2 -1
  13. package/dist/ProfileTypeSelector/index.js +2 -2
  14. package/dist/ProfileView/components/DiffLegend.js +2 -2
  15. package/dist/ProfileView/components/GroupByLabelsDropdown/index.js +3 -3
  16. package/dist/ProfileView/components/ProfileFilters/index.js +6 -6
  17. package/dist/ProfileView/components/Toolbars/index.js +2 -2
  18. package/dist/SimpleMatchers/index.js +3 -3
  19. package/dist/ViewMatchers/index.js +2 -2
  20. package/package.json +4 -4
  21. package/src/MatchersInput/index.tsx +3 -3
  22. package/src/MetricsGraph/MetricsContextMenu/index.tsx +2 -2
  23. package/src/MetricsGraph/MetricsTooltip/index.tsx +2 -2
  24. package/src/MetricsGraph/index.tsx +2 -2
  25. package/src/ProfileExplorer/ProfileExplorerCompare.tsx +11 -5
  26. package/src/ProfileFlameGraph/index.tsx +2 -2
  27. package/src/ProfileMetricsGraph/index.tsx +4 -4
  28. package/src/ProfileSelector/QueryControls.tsx +13 -13
  29. package/src/ProfileSelector/index.tsx +6 -1
  30. package/src/ProfileTypeSelector/index.tsx +2 -2
  31. package/src/ProfileView/components/DiffLegend.tsx +2 -2
  32. package/src/ProfileView/components/GroupByLabelsDropdown/index.tsx +4 -4
  33. package/src/ProfileView/components/ProfileFilters/index.tsx +10 -10
  34. package/src/ProfileView/components/Toolbars/index.tsx +2 -2
  35. package/src/SimpleMatchers/index.tsx +9 -9
  36. package/src/ViewMatchers/index.tsx +2 -2
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.19.55](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.54...@parca/profile@0.19.55) (2025-09-16)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
10
+ ## [0.19.54](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.53...@parca/profile@0.19.54) (2025-09-16)
11
+
12
+ **Note:** Version bump only for package @parca/profile
13
+
6
14
  ## [0.19.53](https://github.com/parca-dev/parca/compare/@parca/profile@0.19.52...@parca/profile@0.19.53) (2025-09-15)
7
15
 
8
16
  ## 0.24.2 (2025-09-10)
@@ -17,7 +17,7 @@ import cx from 'classnames';
17
17
  import TextareaAutosize from 'react-textarea-autosize';
18
18
  import { useGrpcMetadata } from '@parca/components';
19
19
  import { Query } from '@parca/parser';
20
- import { testId } from '@parca/test-utils';
20
+ import { TEST_IDS, testId } from '@parca/test-utils';
21
21
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
22
22
  import { LabelsProvider, useLabels } from '../contexts/MatchersInputLabelsContext';
23
23
  import useGrpcQuery from '../useGrpcQuery';
@@ -194,9 +194,9 @@ const MatchersInput = ({ setMatchersString, runQuery, currentQuery, }) => {
194
194
  setFocusedInput(false);
195
195
  };
196
196
  const profileSelected = currentQuery.profileName() === '';
197
- return (_jsxs("div", { className: "w-full min-w-[300px] flex-1 font-mono relative", ...testId('MATCHERS_INPUT_CONTAINER'), children: [_jsx(TextareaAutosize, { ref: inputRef, className: cx('block h-[38px] w-full flex-1 rounded-md border bg-white px-2 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected
197
+ return (_jsxs("div", { className: "w-full min-w-[300px] flex-1 font-mono relative", ...testId(TEST_IDS.MATCHERS_INPUT_CONTAINER), children: [_jsx(TextareaAutosize, { ref: inputRef, className: cx('block h-[38px] w-full flex-1 rounded-md border bg-white px-2 py-2 text-sm shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected
198
198
  ? 'Select a profile first to enter a filter...'
199
- : 'filter profiles... eg. node="test"', onChange: onChange, value: value, onBlur: unfocus, ...testId('MATCHERS_TEXTAREA'), onFocus: focus, disabled: profileSelected, title: profileSelected
199
+ : 'filter profiles... eg. node="test"', onChange: onChange, value: value, onBlur: unfocus, ...testId(TEST_IDS.MATCHERS_TEXTAREA), onFocus: focus, disabled: profileSelected, title: profileSelected
200
200
  ? 'Select a profile first to enter a filter...'
201
201
  : 'filter profiles... eg. node="test"', id: "matchers-input" }), _jsx(SuggestionsList, { isLabelNamesLoading: isLabelNamesLoading, suggestions: suggestionSections, applySuggestion: applySuggestion, inputRef: inputRef.current, runQuery: runQuery, focusedInput: focusedInput, isLabelValuesLoading: isLabelValuesLoading && lastCompleted.type === 'literal', shouldTrimPrefix: shouldHandlePrefixes })] }));
202
202
  };
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  import { Icon } from '@iconify/react';
15
15
  import { Item, Menu, Submenu } from 'react-contexify';
16
16
  import { useParcaContext } from '@parca/components';
17
- import { testId } from '@parca/test-utils';
17
+ import { TEST_IDS, testId } from '@parca/test-utils';
18
18
  const MetricsContextMenu = ({ menuId, closestPoint, series, trackVisibility, menuItems, }) => {
19
19
  const { isDarkMode } = useParcaContext();
20
20
  const renderMenuItem = (item) => {
@@ -32,6 +32,6 @@ const MetricsContextMenu = ({ menuId, closestPoint, series, trackVisibility, men
32
32
  return (_jsx(Item, { id: menuItem.id, onClick: () => menuItem.onClick(closestPoint, series), disabled: menuItem.disabled?.(closestPoint, series) ?? false, children: _jsxs("div", { className: "flex w-full items-center gap-2", children: [menuItem.icon != null && menuItem.icon !== '' && _jsx(Icon, { icon: menuItem.icon }), _jsx("div", { children: menuItem.label })] }) }, menuItem.id));
33
33
  }
34
34
  };
35
- return (_jsx(Menu, { id: menuId, onVisibilityChange: trackVisibility, theme: isDarkMode ? 'dark' : '', ...testId('METRICS_GRAPH_CONTEXT_MENU'), children: menuItems.map(renderMenuItem) }));
35
+ return (_jsx(Menu, { id: menuId, onVisibilityChange: trackVisibility, theme: isDarkMode ? 'dark' : '', ...testId(TEST_IDS.METRICS_GRAPH_CONTEXT_MENU), children: menuItems.map(renderMenuItem) }));
36
36
  };
37
37
  export default MetricsContextMenu;
@@ -13,7 +13,7 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
13
13
  // limitations under the License.
14
14
  import { useEffect, useMemo, useState } from 'react';
15
15
  import { usePopper } from 'react-popper';
16
- import { testId } from '@parca/test-utils';
16
+ import { TEST_IDS, testId } from '@parca/test-utils';
17
17
  const virtualElement = {
18
18
  getBoundingClientRect: () => {
19
19
  const emptyRect = {
@@ -87,6 +87,6 @@ const MetricsTooltip = ({ x, y, contextElement, content }) => {
87
87
  if (content == null) {
88
88
  return _jsx(_Fragment, {});
89
89
  }
90
- return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, ...testId('METRICS_GRAPH_TOOLTIP'), className: "z-50", children: _jsx("div", { className: "flex max-w-lg", children: _jsx("div", { className: "m-auto", children: _jsx("div", { className: "border border-gray-300 bg-gray-50 dark:border-gray-500 dark:bg-gray-900 rounded-lg shadow-lg px-3 py-2", children: content }) }) }) }));
90
+ return (_jsx("div", { ref: setPopperElement, style: styles.popper, ...attributes.popper, ...testId(TEST_IDS.METRICS_GRAPH_TOOLTIP), className: "z-50", children: _jsx("div", { className: "flex max-w-lg", children: _jsx("div", { className: "m-auto", children: _jsx("div", { className: "border border-gray-300 bg-gray-50 dark:border-gray-500 dark:bg-gray-900 rounded-lg shadow-lg px-3 py-2", children: content }) }) }) }));
91
91
  };
92
92
  export default MetricsTooltip;
@@ -17,7 +17,7 @@ import { pointer } from 'd3-selection';
17
17
  import throttle from 'lodash.throttle';
18
18
  import { useContextMenu } from 'react-contexify';
19
19
  import { DateTimeRange, useParcaContext } from '@parca/components';
20
- import { testId } from '@parca/test-utils';
20
+ import { TEST_IDS, testId } from '@parca/test-utils';
21
21
  import { formatDate, formatForTimespan, getPrecision, valueFormatter } from '@parca/utilities';
22
22
  import MetricsCircle from '../MetricsCircle';
23
23
  import MetricsSeries from '../MetricsSeries';
@@ -26,7 +26,7 @@ import MetricsInfoPanel from './MetricsInfoPanel';
26
26
  import MetricsTooltip from './MetricsTooltip';
27
27
  const MetricsGraph = ({ data, from, to, onSampleClick, setTimeRange, yAxisLabel, yAxisUnit, width = 0, height = 0, margin = 0, selectedPoint, contextMenuItems, renderTooltipContent, }) => {
28
28
  const [isInfoPanelOpen, setIsInfoPanelOpen] = useState(false);
29
- return (_jsxs("div", { className: "relative", ...testId('METRICS_GRAPH'), onClick: () => isInfoPanelOpen && setIsInfoPanelOpen(false), children: [_jsx("div", { className: "absolute right-0 top-0", children: _jsx(MetricsInfoPanel, { isInfoPanelOpen: isInfoPanelOpen, onInfoIconClick: () => setIsInfoPanelOpen(true) }) }), _jsx(RawMetricsGraph, { data: data, from: from, to: to, onSampleClick: onSampleClick, setTimeRange: setTimeRange, yAxisLabel: yAxisLabel, yAxisUnit: yAxisUnit, width: width, height: height, margin: margin, selectedPoint: selectedPoint, contextMenuItems: contextMenuItems, renderTooltipContent: renderTooltipContent })] }));
29
+ return (_jsxs("div", { className: "relative", ...testId(TEST_IDS.METRICS_GRAPH), onClick: () => isInfoPanelOpen && setIsInfoPanelOpen(false), children: [_jsx("div", { className: "absolute right-0 top-0", children: _jsx(MetricsInfoPanel, { isInfoPanelOpen: isInfoPanelOpen, onInfoIconClick: () => setIsInfoPanelOpen(true) }) }), _jsx(RawMetricsGraph, { data: data, from: from, to: to, onSampleClick: onSampleClick, setTimeRange: setTimeRange, yAxisLabel: yAxisLabel, yAxisUnit: yAxisUnit, width: width, height: height, margin: margin, selectedPoint: selectedPoint, contextMenuItems: contextMenuItems, renderTooltipContent: renderTooltipContent })] }));
30
30
  };
31
31
  export default MetricsGraph;
32
32
  export const parseValue = (value) => {
@@ -1 +1 @@
1
- {"version":3,"file":"ProfileExplorerCompare.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/ProfileExplorerCompare.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAIjD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAoB,gBAAgB,EAAsB,MAAM,IAAI,CAAC;AAC5E,OAAwB,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAEnE,UAAU,2BAA2B;IACnC,WAAW,EAAE,kBAAkB,CAAC;IAEhC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,QAAA,MAAM,sBAAsB,GAAI,4IAY7B,2BAA2B,KAAG,GAAG,CAAC,OAyEpC,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"ProfileExplorerCompare.d.ts","sourceRoot":"","sources":["../../src/ProfileExplorer/ProfileExplorerCompare.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAIjD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAoB,gBAAgB,EAAsB,MAAM,IAAI,CAAC;AAC5E,OAAwB,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAEnE,UAAU,2BAA2B;IACnC,WAAW,EAAE,kBAAkB,CAAC;IAEhC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,YAAY,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnD,cAAc,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACnD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,QAAA,MAAM,sBAAsB,GAAI,4IAY7B,2BAA2B,KAAG,GAAG,CAAC,OA+EpC,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -14,7 +14,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  import { useState } from 'react';
15
15
  import { useURLState } from '@parca/components';
16
16
  import { Query } from '@parca/parser';
17
- import { testId } from '@parca/test-utils';
17
+ import { TEST_IDS, testId } from '@parca/test-utils';
18
18
  import { ProfileDiffSource, ProfileViewWithData } from '..';
19
19
  import ProfileSelector from '../ProfileSelector';
20
20
  const ProfileExplorerCompare = ({ queryClient, queryA, queryB, profileA, profileB, selectQueryA, selectQueryB, selectProfileA, selectProfileB, closeProfile, navigateTo, }) => {
@@ -26,6 +26,6 @@ const ProfileExplorerCompare = ({ queryClient, queryA, queryB, profileA, profile
26
26
  closeProfile('B');
27
27
  };
28
28
  const [compareAbsolute] = useURLState('compare_absolute');
29
- return (_jsxs("div", { ...testId('COMPARE_CONTAINER'), children: [_jsxs("div", { className: "flex justify-between gap-2 relative mb-2", children: [_jsx("div", { className: "flex-column flex-1 p-2 shadow-md rounded-md", ...testId('COMPARE_SIDE_A'), children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true, navigateTo: navigateTo, suffix: "_a", showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) }), _jsx("div", { className: "flex-column flex-1 p-2 shadow-md rounded-md", ...testId('COMPARE_SIDE_B'), children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true, navigateTo: navigateTo, suffix: "_b", showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { ...testId('COMPARE_PROFILE_VIEW'), children: _jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource(), compareAbsolute === 'true') }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
29
+ return (_jsxs("div", { ...testId(TEST_IDS.COMPARE_CONTAINER), children: [_jsxs("div", { className: "flex justify-between gap-2 relative mb-2", children: [_jsx("div", { className: "flex-column flex-1 p-2 shadow-md rounded-md", ...testId(TEST_IDS.COMPARE_SIDE_A), children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryA, profileSelection: profileA, selectProfile: selectProfileA, selectQuery: selectQueryA, closeProfile: closeProfileA, enforcedProfileName: '', comparing: true, navigateTo: navigateTo, suffix: "_a", showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) }), _jsx("div", { className: "flex-column flex-1 p-2 shadow-md rounded-md", ...testId(TEST_IDS.COMPARE_SIDE_B), children: _jsx(ProfileSelector, { queryClient: queryClient, querySelection: queryB, profileSelection: profileB, selectProfile: selectProfileB, selectQuery: selectQueryB, closeProfile: closeProfileB, enforcedProfileName: Query.parse(queryA.expression).profileName(), comparing: true, navigateTo: navigateTo, suffix: "_b", showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setShowMetricsGraph }) })] }), _jsx("div", { className: "grid grid-cols-1", children: profileA != null && profileB != null ? (_jsx("div", { ...testId(TEST_IDS.COMPARE_PROFILE_VIEW), children: _jsx(ProfileViewWithData, { queryClient: queryClient, profileSource: new ProfileDiffSource(profileA.ProfileSource(), profileB.ProfileSource(), compareAbsolute === 'true') }) })) : (_jsx("div", { children: _jsx("div", { className: "my-20 text-center", children: _jsx("p", { children: "Select a profile on both sides." }) }) })) })] }));
30
30
  };
31
31
  export default ProfileExplorerCompare;
@@ -16,7 +16,7 @@ import cx from 'classnames';
16
16
  import { AnimatePresence, motion } from 'framer-motion';
17
17
  import { useMeasure } from 'react-use';
18
18
  import { FlameGraphSkeleton, SandwichFlameGraphSkeleton, useParcaContext, useURLState, } from '@parca/components';
19
- import { testId } from '@parca/test-utils';
19
+ import { TEST_IDS, testId } from '@parca/test-utils';
20
20
  import { capitalizeOnlyFirstLetter, divide } from '@parca/utilities';
21
21
  import DiffLegend from '../ProfileView/components/DiffLegend';
22
22
  import { useProfileViewContext } from '../ProfileView/context/ProfileViewContext';
@@ -173,6 +173,6 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({ arrow, total, filt
173
173
  }
174
174
  return (_jsx(ErrorContent, { errorMessage: _jsxs(_Fragment, { children: [_jsx("span", { children: capitalizeOnlyFirstLetter(error.message) }), isFlameChart ? flamechartHelpText ?? null : null] }) }));
175
175
  }
176
- return (_jsx(AnimatePresence, { children: _jsxs(motion.div, { className: "relative h-full w-full", initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.5 }, children: [compareMode ? _jsx(DiffLegend, {}) : null, _jsx("div", { className: cx(!isInSandwichView ? 'min-h-48' : ''), id: "h-flame-graph", ...testId('FLAMEGRAPH_CONTAINER'), children: _jsx(_Fragment, { children: flameGraph }) }), !isInSandwichView && (_jsxs("p", { className: "my-2 text-xs", children: ["Showing ", totalFormatted, ' ', isFiltered ? (_jsxs("span", { children: ["(", filteredPercentage, "%) filtered of ", totalUnfilteredFormatted, ' '] })) : (_jsx(_Fragment, {})), "values.", ' '] }))] }, "flame-graph-loaded") }));
176
+ return (_jsx(AnimatePresence, { children: _jsxs(motion.div, { className: "relative h-full w-full", initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.5 }, children: [compareMode ? _jsx(DiffLegend, {}) : null, _jsx("div", { className: cx(!isInSandwichView ? 'min-h-48' : ''), id: "h-flame-graph", ...testId(TEST_IDS.FLAMEGRAPH_CONTAINER), children: _jsx(_Fragment, { children: flameGraph }) }), !isInSandwichView && (_jsxs("p", { className: "my-2 text-xs", children: ["Showing ", totalFormatted, ' ', isFiltered ? (_jsxs("span", { children: ["(", filteredPercentage, "%) filtered of ", totalUnfilteredFormatted, ' '] })) : (_jsx(_Fragment, {})), "values.", ' '] }))] }, "flame-graph-loaded") }));
177
177
  };
178
178
  export default ProfileFlameGraph;
@@ -16,7 +16,7 @@ import { Icon } from '@iconify/react';
16
16
  import { AnimatePresence, motion } from 'framer-motion';
17
17
  import { MetricsGraphSkeleton, TextWithTooltip, useParcaContext, } from '@parca/components';
18
18
  import { Query } from '@parca/parser';
19
- import { testId } from '@parca/test-utils';
19
+ import { TEST_IDS, testId } from '@parca/test-utils';
20
20
  import { capitalizeOnlyFirstLetter, formatDate, timePattern, valueFormatter } from '@parca/utilities';
21
21
  import { MergedProfileSelection } from '..';
22
22
  import MetricsGraph from '../MetricsGraph';
@@ -268,9 +268,9 @@ const ProfileMetricsGraph = ({ queryClient, queryExpression, profile, from, to,
268
268
  return (_jsx("div", { className: "flex flex-row", children: _jsxs("div", { className: "ml-2 mr-6", children: [_jsx("span", { className: "font-semibold", children: highlightedNameLabel.value }), _jsx("span", { className: "my-2 block text-gray-700 dark:text-gray-300", children: _jsx("table", { className: "table-auto", children: _jsxs("tbody", { children: [isDeltaType ? (_jsxs(_Fragment, { children: [_jsxs("tr", { children: [_jsx("td", { className: "w-1/4 pr-3", children: "Per\u00A0Second" }), _jsx("td", { className: "w-3/4", children: valueFormatter(originalPoint.valuePerSecond, sampleUnit === 'nanoseconds' && sampleType === 'cpu'
269
269
  ? 'CPU Cores'
270
270
  : sampleUnit, 5) })] }), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Total" }), _jsx("td", { className: "w-3/4", children: valueFormatter(originalPoint.value ?? 0, sampleUnit, 2) })] })] })) : (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Value" }), _jsx("td", { className: "w-3/4", children: valueFormatter(originalPoint.valuePerSecond, sampleUnit, 5) })] })), originalPoint.duration != null &&
271
- Number(originalPoint.duration) > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Duration" }), _jsx("td", { className: "w-3/4", children: valueFormatter(Number(originalPoint.duration.toString()), 'nanoseconds', 2) })] })), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "At" }), _jsx("td", { className: "w-3/4", children: formatDate(new Date(timestampMs), timePattern(timezone), timezone) })] })] }) }) }), _jsx("span", { className: "my-2 block text-gray-500", children: utilizationMetrics ? (_jsxs(_Fragment, { children: [Object.keys(attributesResourceMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Resource Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesResourceMap).map(name => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", ...testId('TOOLTIP_LABEL'), children: _jsx(TextWithTooltip, { text: `${name.replace('attributes.', '')}="${attributesResourceMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesResourceMap[name]}` }) }, name))) }), Object.keys(attributesMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesMap).map(name => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", ...testId('TOOLTIP_LABEL'), children: _jsx(TextWithTooltip, { text: `${name.replace('attributes.', '')}="${attributesMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesMap[name]}` }) }, name))) })] })) : (_jsx(_Fragment, { children: labels
271
+ Number(originalPoint.duration) > 0 && (_jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "Duration" }), _jsx("td", { className: "w-3/4", children: valueFormatter(Number(originalPoint.duration.toString()), 'nanoseconds', 2) })] })), _jsxs("tr", { children: [_jsx("td", { className: "w-1/4", children: "At" }), _jsx("td", { className: "w-3/4", children: formatDate(new Date(timestampMs), timePattern(timezone), timezone) })] })] }) }) }), _jsx("span", { className: "my-2 block text-gray-500", children: utilizationMetrics ? (_jsxs(_Fragment, { children: [Object.keys(attributesResourceMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Resource Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesResourceMap).map(name => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", ...testId(TEST_IDS.TOOLTIP_LABEL), children: _jsx(TextWithTooltip, { text: `${name.replace('attributes.', '')}="${attributesResourceMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesResourceMap[name]}` }) }, name))) }), Object.keys(attributesMap).length > 0 && (_jsx("span", { className: "text-sm font-bold text-gray-700 dark:text-white", children: "Attributes" })), _jsx("span", { className: "my-2 block text-gray-500", children: Object.keys(attributesMap).map(name => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", ...testId(TEST_IDS.TOOLTIP_LABEL), children: _jsx(TextWithTooltip, { text: `${name.replace('attributes.', '')}="${attributesMap[name]}"`, maxTextLength: 48, id: `tooltip-${name}-${attributesMap[name]}` }) }, name))) })] })) : (_jsx(_Fragment, { children: labels
272
272
  .filter((label) => label.name !== '__name__')
273
- .map((label) => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", ...testId('TOOLTIP_LABEL'), children: _jsx(TextWithTooltip, { text: `${label.name}="${label.value}"`, maxTextLength: 37, id: `tooltip-${label.name}` }) }, label.name))) })) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to add labels to query." })] })] }) }));
273
+ .map((label) => (_jsx("div", { className: "mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400", ...testId(TEST_IDS.TOOLTIP_LABEL), children: _jsx(TextWithTooltip, { text: `${label.name}="${label.value}"`, maxTextLength: 37, id: `tooltip-${label.name}` }) }, label.name))) })) }), _jsxs("div", { className: "flex w-full items-center gap-1 text-xs text-gray-500", children: [_jsx(Icon, { icon: "iconoir:mouse-button-right" }), _jsx("div", { children: "Right click to add labels to query." })] })] }) }));
274
274
  }
275
275
  }
276
276
  return null;
@@ -14,19 +14,19 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
14
14
  import { Switch } from '@headlessui/react';
15
15
  import Select from 'react-select';
16
16
  import { Button, DateTimeRangePicker, useParcaContext } from '@parca/components';
17
- import { testId } from '@parca/test-utils';
17
+ import { TEST_IDS, testId } from '@parca/test-utils';
18
18
  import MatchersInput from '../MatchersInput';
19
19
  import ProfileTypeSelector from '../ProfileTypeSelector';
20
20
  import SimpleMatchers from '../SimpleMatchers';
21
21
  import ViewMatchers from '../ViewMatchers';
22
22
  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, }) {
23
23
  const { timezone } = useParcaContext();
24
- return (_jsxs("div", { className: "flex w-full flex-wrap items-start gap-2", ...testId('QUERY_CONTROLS_CONTAINER'), children: [showProfileTypeSelector && (_jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId('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('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('QUERY_LABEL'), children: "Query" }), viewComponent?.disableExplorativeQuerying !== true && (_jsxs(_Fragment, { children: [_jsxs(Switch, { checked: advancedModeForQueryBrowser, onChange: () => {
24
+ 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: () => {
25
25
  setAdvancedModeForQueryBrowser(!advancedModeForQueryBrowser);
26
26
  setQueryBrowserMode(advancedModeForQueryBrowser ? 'simple' : 'advanced');
27
- }, 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('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('QUERY_MODE_LABEL'), children: "Advanced Mode" })] }))] }), viewComponent?.createViewComponent] }), viewComponent?.disableExplorativeQuerying === true &&
27
+ }, 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 &&
28
28
  viewComponent?.labelnames !== undefined &&
29
- 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() }, query.toString()))] }), showSumBySelector && (_jsxs("div", { ...testId('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('SUM_BY_LABEL'), children: "Sum by" }) }), _jsx(Select, { id: "h-sum-by-selector", "data-testid": testId('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 => {
29
+ 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() }, query.toString()))] }), 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(Select, { 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 => {
30
30
  setUserSumBySelection(newValue.map(option => option.value));
31
31
  }, placeholder: "Labels...", styles: {
32
32
  indicatorSeparator: () => ({ display: 'none' }),
@@ -49,8 +49,8 @@ export function QueryControls({ showProfileTypeSelector, profileTypesData, profi
49
49
  setQueryExpression(true);
50
50
  currentRef.blur();
51
51
  }
52
- } })] })), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection, timezone: timezone, ...testId('DATE_TIME_RANGE_PICKER') }), _jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId('SEARCH_BUTTON_LABEL'), children: "\u00A0" }), _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
52
+ } })] })), _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) => {
53
53
  e.preventDefault();
54
54
  setQueryExpression(true);
55
- }, id: "h-matcher-search-button", ...testId('SEARCH_BUTTON'), children: "Search" })] })] }));
55
+ }, id: "h-matcher-search-button", ...testId(TEST_IDS.SEARCH_BUTTON), children: "Search" })] })] }));
56
56
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
15
15
  import { DateTimeRange, IconButton, useGrpcMetadata, useParcaContext, useURLState, } from '@parca/components';
16
16
  import { CloseIcon } from '@parca/icons';
17
17
  import { Query } from '@parca/parser';
18
+ import { TEST_IDS, testId } from '@parca/test-utils';
18
19
  import { useLabelNames } from '../MatchersInput/index';
19
20
  import { useMetricsGraphDimensions } from '../MetricsGraph/useMetricsGraphDimensions';
20
21
  import { UtilizationLabelsProvider } from '../contexts/UtilizationLabelsContext';
@@ -142,7 +143,7 @@ const ProfileSelector = ({ queryClient, querySelection, selectProfile, selectQue
142
143
  queryExpressionString === '{}';
143
144
  const queryBrowserRef = useRef(null);
144
145
  const sumByRef = useRef(null);
145
- return (_jsx(UtilizationLabelsProvider, { value: { ...utilizationLabels }, children: _jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, disableExplorativeQuerying: disableExplorativeQuerying, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: setTimeRangeSelection, searchDisabled: searchDisabled, queryBrowserMode: queryBrowserMode, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: sumBySelection ?? [], sumBySelectionLoading: sumBySelectionLoading, setUserSumBySelection: setUserSumBySelection, profileType: profileType, profileTypesError: error, viewComponent: viewComponent }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: utilizationMetrics !== undefined && utilizationMetrics?.length > 0
146
+ return (_jsx(UtilizationLabelsProvider, { value: { ...utilizationLabels }, children: _jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-2 flex", children: [_jsx(QueryControls, { showProfileTypeSelector: showProfileTypeSelector, showSumBySelector: showSumBySelector, disableExplorativeQuerying: disableExplorativeQuerying, profileTypesData: profileTypesData, profileTypesLoading: profileTypesLoading, selectedProfileName: selectedProfileName, setProfileName: setProfileName, setMatchersString: setMatchersString, setQueryExpression: setQueryExpression, query: query, queryBrowserRef: queryBrowserRef, timeRangeSelection: timeRangeSelection, setTimeRangeSelection: setTimeRangeSelection, searchDisabled: searchDisabled, queryBrowserMode: queryBrowserMode, setQueryBrowserMode: setQueryBrowserMode, advancedModeForQueryBrowser: advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser: setAdvancedModeForQueryBrowser, queryClient: queryClient, sumByRef: sumByRef, labels: labels, sumBySelection: sumBySelection ?? [], sumBySelectionLoading: sumBySelectionLoading, setUserSumBySelection: setUserSumBySelection, profileType: profileType, profileTypesError: error, viewComponent: viewComponent }), comparing && (_jsx("div", { children: _jsx(IconButton, { onClick: () => closeProfile(), icon: _jsx(CloseIcon, {}), ...testId(TEST_IDS.COMPARE_CLOSE_BUTTON) }) }))] }), _jsx(MetricsGraphSection, { showMetricsGraph: showMetricsGraph, setDisplayHideMetricsGraphButton: setDisplayHideMetricsGraphButton, heightStyle: utilizationMetrics !== undefined && utilizationMetrics?.length > 0
146
147
  ? 'auto'
147
148
  : heightStyle, querySelection: querySelection, profileSelection: profileSelection, comparing: comparing, sumBy: querySelection.sumBy ?? defaultSumBy ?? [], defaultSumByLoading: defaultSumByLoading, queryClient: queryClient, queryExpressionString: queryExpressionString, setTimeRangeSelection: setTimeRangeSelection, selectQuery: selectQuery, selectProfile: selectProfile, query: query, setQueryExpression: setQueryExpression, setNewQueryExpression: setNewQueryExpression, utilizationMetrics: utilizationMetrics, utilizationMetricsLoading: utilizationMetricsLoading, onUtilizationSeriesSelect: onUtilizationSeriesSelect })] }) }));
148
149
  };
@@ -13,7 +13,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
13
13
  // limitations under the License.
14
14
  import { useMemo } from 'react';
15
15
  import { Select } from '@parca/components';
16
- import { testId } from '@parca/test-utils';
16
+ import { TEST_IDS, testId } from '@parca/test-utils';
17
17
  export const wellKnownProfiles = {
18
18
  'block:contentions:count:contentions:count': {
19
19
  name: 'Block Contentions Total',
@@ -139,6 +139,6 @@ const ProfileTypeSelector = ({ profileTypesData, loading = false, error, selecte
139
139
  key: name,
140
140
  element: profileSelectElement(name, flexibleKnownProfilesDetection),
141
141
  }));
142
- return (_jsx(Select, { items: profileLabels, selectedKey: selectedKey, onSelection: onSelection, placeholder: "Select profile type...", loading: loading, className: "bg-white h-profile-type-dropdown", disabled: disabled, ...testId('PROFILE_TYPE_SELECTOR') }));
142
+ return (_jsx(Select, { items: profileLabels, selectedKey: selectedKey, onSelection: onSelection, placeholder: "Select profile type...", loading: loading, className: "bg-white h-profile-type-dropdown", disabled: disabled, ...testId(TEST_IDS.PROFILE_TYPE_SELECTOR) }));
143
143
  };
144
144
  export default ProfileTypeSelector;
@@ -15,7 +15,7 @@ import { Fragment, useState } from 'react';
15
15
  import { Popover, Transition } from '@headlessui/react';
16
16
  import { usePopper } from 'react-popper';
17
17
  import { selectDarkMode, useAppSelector } from '@parca/store';
18
- import { testId } from '@parca/test-utils';
18
+ import { TEST_IDS, testId } from '@parca/test-utils';
19
19
  import { getIncreasedSpanColor, getNewSpanColor, getReducedSpanColor } from '@parca/utilities';
20
20
  const transparencyValues = [-100, -80, -60, -40, -20, 0, 20, 40, 60, 80, 100];
21
21
  const DiffLegendBar = ({ onMouseEnter, onMouseLeave, }) => {
@@ -46,6 +46,6 @@ const DiffLegend = () => {
46
46
  const handleMouseLeave = () => {
47
47
  setShowLegendTooltip(false);
48
48
  };
49
- return (_jsxs("div", { className: "mt-1 mb-2 hidden md:block", id: "h-diff-legend", ...testId('DIFF_LEGEND'), children: [_jsxs("div", { ref: setReferenceElement, className: "flex items-center justify-center", children: [_jsx("span", { children: "Better" }), _jsx(DiffLegendBar, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }), _jsx("span", { children: "Worse" })] }), _jsx(Popover, { className: "relative", children: () => (_jsx(Transition, { show: showLegendTooltip, as: Fragment, enter: "transition ease-out duration-200", enterFrom: "opacity-0 translate-y-1", enterTo: "opacity-100 translate-y-0", leave: "transition ease-in duration-150", leaveFrom: "opacity-100 translate-y-0", leaveTo: "opacity-0 translate-y-1", children: _jsx(Popover.Panel, { ref: setPopperElement, style: styles.popper, ...attributes.popper, children: _jsx("div", { className: "overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5", children: _jsxs("div", { className: "bg-gray-50 p-4 dark:bg-gray-800", children: [_jsx("div", { className: "flex items-center justify-center" }), _jsx("span", { className: "block text-sm text-gray-500 dark:text-gray-50", children: "This is a differential flame graph, where a purple-colored node means unchanged, and the darker the red, the worse the node got, and the darker the green, the better the node got." })] }) }) }) })) })] }));
49
+ return (_jsxs("div", { className: "mt-1 mb-2 hidden md:block", id: "h-diff-legend", ...testId(TEST_IDS.DIFF_LEGEND), children: [_jsxs("div", { ref: setReferenceElement, className: "flex items-center justify-center", children: [_jsx("span", { children: "Better" }), _jsx(DiffLegendBar, { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave }), _jsx("span", { children: "Worse" })] }), _jsx(Popover, { className: "relative", children: () => (_jsx(Transition, { show: showLegendTooltip, as: Fragment, enter: "transition ease-out duration-200", enterFrom: "opacity-0 translate-y-1", enterTo: "opacity-100 translate-y-0", leave: "transition ease-in duration-150", leaveFrom: "opacity-100 translate-y-0", leaveTo: "opacity-0 translate-y-1", children: _jsx(Popover.Panel, { ref: setPopperElement, style: styles.popper, ...attributes.popper, children: _jsx("div", { className: "overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5", children: _jsxs("div", { className: "bg-gray-50 p-4 dark:bg-gray-800", children: [_jsx("div", { className: "flex items-center justify-center" }), _jsx("span", { className: "block text-sm text-gray-500 dark:text-gray-50", children: "This is a differential flame graph, where a purple-colored node means unchanged, and the darker the red, the worse the node got, and the darker the green, the better the node got." })] }) }) }) })) })] }));
50
50
  };
51
51
  export default DiffLegend;
@@ -12,12 +12,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
  import Select from 'react-select';
15
- import { testId } from '@parca/test-utils';
15
+ import { TEST_IDS, testId } from '@parca/test-utils';
16
16
  import { FIELD_LABELS } from '../../../ProfileFlameGraph/FlameGraphArrow';
17
17
  const GroupByLabelsDropdown = ({ labels, groupBy, setGroupByLabels }) => {
18
- return (_jsxs("div", { className: "flex flex-col relative", ...testId('GROUP_BY_CONTAINER'), children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("label", { className: "text-sm", ...testId('GROUP_BY_LABEL'), children: "Group by" }) }), _jsx(Select, { isMulti: true, defaultMenuIsOpen: false, defaultValue: undefined, name: "labels", options: labels.map(label => ({ label, value: `${FIELD_LABELS}.${label}` })), className: "parca-select-container text-sm rounded-md bg-white", classNamePrefix: "parca-select", components: {
18
+ return (_jsxs("div", { className: "flex flex-col relative", ...testId(TEST_IDS.GROUP_BY_CONTAINER), children: [_jsx("div", { className: "flex items-center justify-between", children: _jsx("label", { className: "text-sm", ...testId(TEST_IDS.GROUP_BY_LABEL), children: "Group by" }) }), _jsx(Select, { isMulti: true, defaultMenuIsOpen: false, defaultValue: undefined, name: "labels", options: labels.map(label => ({ label, value: `${FIELD_LABELS}.${label}` })), className: "parca-select-container text-sm rounded-md bg-white", classNamePrefix: "parca-select", components: {
19
19
  // eslint-disable-next-line react/prop-types
20
- MenuList: ({ children, innerProps }) => (_jsx("div", { className: "overflow-y-auto", ...testId('GROUP_BY_SELECT_FLYOUT'), ...innerProps,
20
+ MenuList: ({ children, innerProps }) => (_jsx("div", { className: "overflow-y-auto", ...testId(TEST_IDS.GROUP_BY_SELECT_FLYOUT), ...innerProps,
21
21
  // eslint-disable-next-line react/prop-types
22
22
  style: { ...innerProps.style, height: '332px', maxHeight: '332px', fontSize: '14px' }, children: children })),
23
23
  }, value: groupBy
@@ -15,7 +15,7 @@ import { useCallback } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
17
  import { Button, Input, Select } from '@parca/components';
18
- import { testId } from '@parca/test-utils';
18
+ import { TEST_IDS, testId } from '@parca/test-utils';
19
19
  import { useProfileViewContext } from '../../context/ProfileViewContext';
20
20
  import { getPresetByKey, getPresetsForProfileType, isPresetKey } from './filterPresets';
21
21
  import { useProfileFilters } from './useProfileFilters';
@@ -155,11 +155,11 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
155
155
  }
156
156
  }, [onApplyFilters]);
157
157
  const filtersToRender = localFilters.length > 0 ? localFilters : appliedFilters ?? [];
158
- return (_jsxs("div", { className: "flex gap-2 w-full items-start", ...testId('PROFILE_FILTERS_CONTAINER'), children: [_jsxs("div", { className: "flex-1 flex flex-wrap gap-2", children: [filtersToRender.map(filter => {
158
+ return (_jsxs("div", { className: "flex gap-2 w-full items-start", ...testId(TEST_IDS.PROFILE_FILTERS_CONTAINER), children: [_jsxs("div", { className: "flex-1 flex flex-wrap gap-2", children: [filtersToRender.map(filter => {
159
159
  const isNumberField = filter.field === 'address' || filter.field === 'line_number';
160
160
  const matchTypeItems = isNumberField ? numberMatchTypeItems : stringMatchTypeItems;
161
161
  const isPresetFilter = filter.type != null && isPresetKey(filter.type);
162
- return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", disabled: readOnly, ...testId('FILTER_TYPE_SELECT'), flyoutTestId: "filter-type-select-flyout", onSelection: key => {
162
+ return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Select, { items: filterTypeItems, selectedKey: filter.type, placeholder: "Select Filter", disabled: readOnly, ...testId(TEST_IDS.FILTER_TYPE_SELECT), flyoutTestId: "filter-type-select-flyout", onSelection: key => {
163
163
  // Check if this is a preset selection
164
164
  if (isPresetKey(key)) {
165
165
  const preset = getPresetByKey(key);
@@ -191,7 +191,7 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
191
191
  });
192
192
  }
193
193
  }
194
- }, className: cx('gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1', readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none', !readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'), readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'), hideCaretDropdown: readOnly }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', disabled: readOnly, ...testId('FILTER_FIELD_SELECT'), flyoutTestId: "filter-field-select-flyout", onSelection: key => {
194
+ }, className: cx('gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1', readOnly && isPresetFilter ? 'rounded-md' : 'rounded-l-md rounded-r-none', !readOnly && (isPresetFilter ? 'rounded-r-none border-r-0' : 'rounded-r-none'), readOnly ? 'w-auto' : filter.type != null ? 'border-r-0 w-auto' : 'w-32'), hideCaretDropdown: readOnly }), filter.type != null && !isPresetFilter && (_jsxs(_Fragment, { children: [_jsx(Select, { items: fieldItems, selectedKey: filter.field ?? '', disabled: readOnly, ...testId(TEST_IDS.FILTER_FIELD_SELECT), flyoutTestId: "filter-field-select-flyout", onSelection: key => {
195
195
  const newField = key;
196
196
  const isNewFieldNumber = newField === 'address' || newField === 'line_number';
197
197
  const isCurrentFieldNumber = filter.field === 'address' || filter.field === 'line_number';
@@ -204,7 +204,7 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
204
204
  else {
205
205
  updateFilter(filter.id, { field: newField });
206
206
  }
207
- }, className: cx('rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', disabled: readOnly, ...testId('FILTER_MATCH_TYPE_SELECT'), flyoutTestId: "filter-match-type-select-flyout", onSelection: key => updateFilter(filter.id, { matchType: key }), className: cx('rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Input, { placeholder: "Value", value: filter.value, disabled: readOnly, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1", ...testId('FILTER_VALUE_INPUT') })] })), !readOnly && (_jsx(Button, { variant: "neutral", ...testId('FILTER_REMOVE_BUTTON'), onClick: () => {
207
+ }, className: cx('rounded-none border-r-0 w-32 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Select, { items: matchTypeItems, selectedKey: filter.matchType ?? '', disabled: readOnly, ...testId(TEST_IDS.FILTER_MATCH_TYPE_SELECT), flyoutTestId: "filter-match-type-select-flyout", onSelection: key => updateFilter(filter.id, { matchType: key }), className: cx('rounded-none border-r-0 gap-0 focus:z-50 focus:relative focus:outline-1', readOnly ? '' : 'pr-1'), hideCaretDropdown: readOnly }), _jsx(Input, { placeholder: "Value", value: filter.value, disabled: readOnly, onChange: e => updateFilter(filter.id, { value: e.target.value }), onKeyDown: handleKeyDown, className: "rounded-none w-36 text-sm focus:outline-1", ...testId(TEST_IDS.FILTER_VALUE_INPUT) })] })), !readOnly && (_jsx(Button, { variant: "neutral", ...testId(TEST_IDS.FILTER_REMOVE_BUTTON), onClick: () => {
208
208
  // If we're displaying local filters and this is the last one, reset everything
209
209
  if (localFilters.length > 0 && localFilters.length === 1) {
210
210
  resetFilters();
@@ -220,6 +220,6 @@ const ProfileFilters = ({ readOnly = false } = {}) => {
220
220
  }, className: cx('h-[38px] p-3', filter.type != null
221
221
  ? 'rounded-none rounded-r-md'
222
222
  : 'rounded-l-none rounded-r-md'), children: _jsx(Icon, { icon: "mdi:close", className: "h-4 w-4" }) }))] }, filter.id));
223
- }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", ...testId('ADD_FILTER_BUTTON'), children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), !readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", ...testId('ADD_FILTER_BUTTON'), children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 sticky top-0'), ...testId('APPLY_FILTERS_BUTTON'), children: _jsx("span", { children: "Apply" }) }))] }));
223
+ }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "neutral", onClick: addFilter, className: "p-3 h-[38px]", ...testId(TEST_IDS.ADD_FILTER_BUTTON), children: _jsx(Icon, { icon: "mdi:filter-plus-outline", className: "h-4 w-4" }) })), !readOnly && localFilters.length === 0 && (appliedFilters?.length ?? 0) === 0 && (_jsxs(Button, { variant: "neutral", onClick: addFilter, className: "flex items-center gap-2", ...testId(TEST_IDS.ADD_FILTER_BUTTON), children: [_jsx(Icon, { icon: "mdi:filter-outline", className: "h-4 w-4" }), _jsx("span", { children: "Filter" })] }))] }), !readOnly && localFilters.length > 0 && (_jsx(Button, { variant: "primary", onClick: onApplyFilters, disabled: !hasUnsavedChanges || !localFilters.some(isFilterComplete), className: cx('flex items-center gap-2 sticky top-0'), ...testId(TEST_IDS.APPLY_FILTERS_BUTTON), children: _jsx("span", { children: "Apply" }) }))] }));
224
224
  };
225
225
  export default ProfileFilters;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Icon } from '@iconify/react';
3
3
  import { Button } from '@parca/components';
4
- import { testId } from '@parca/test-utils';
4
+ import { TEST_IDS, testId } from '@parca/test-utils';
5
5
  import { useDashboard } from '../../context/DashboardContext';
6
6
  import GroupByDropdown from '../ActionButtons/GroupByDropdown';
7
7
  import InvertCallStack from '../InvertCallStack';
@@ -14,7 +14,7 @@ export const TableToolbar = ({ profileType, total, filtered }) => {
14
14
  return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end", children: _jsx(TableColumnsDropdown, { profileType: profileType, total: total, filtered: filtered }) }) }));
15
15
  };
16
16
  export const FlameGraphToolbar = ({ curPath, setNewCurPath }) => {
17
- return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end", children: _jsxs(Button, { variant: "neutral", className: "gap-2 w-max h-fit", onClick: () => setNewCurPath([]), disabled: curPath.length === 0, id: "h-reset-graph", ...testId('FLAMEGRAPH_RESET_BUTTON'), children: ["Reset graph", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }) }) }));
17
+ return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end", children: _jsxs(Button, { variant: "neutral", className: "gap-2 w-max h-fit", onClick: () => setNewCurPath([]), disabled: curPath.length === 0, id: "h-reset-graph", ...testId(TEST_IDS.FLAMEGRAPH_RESET_BUTTON), children: ["Reset graph", _jsx(Icon, { icon: "system-uicons:reset", width: 20 })] }) }) }));
18
18
  };
19
19
  export const SandwichFlameGraphToolbar = ({ resetSandwichFunctionName, sandwichFunctionName, }) => {
20
20
  return (_jsx(_Fragment, { children: _jsx("div", { className: "flex w-full gap-2 items-end justify-between", children: _jsx(Button, { color: "neutral", onClick: () => resetSandwichFunctionName(), className: "w-auto", variant: "neutral", disabled: sandwichFunctionName === undefined || sandwichFunctionName.length === 0, children: "Reset view" }) }) }));
@@ -16,7 +16,7 @@ import { Icon } from '@iconify/react';
16
16
  import { useQueryClient } from '@tanstack/react-query';
17
17
  import cx from 'classnames';
18
18
  import { useGrpcMetadata } from '@parca/components';
19
- import { testId } from '@parca/test-utils';
19
+ import { TEST_IDS, testId } from '@parca/test-utils';
20
20
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
21
21
  import { LabelProvider, useLabels } from '../contexts/SimpleMatchersLabelContext';
22
22
  import { useUtilizationLabels } from '../contexts/UtilizationLabelsContext';
@@ -242,9 +242,9 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
242
242
  };
243
243
  }, [queryRows, fetchLabelValuesUnified]);
244
244
  const isRowRegex = (row) => row.operator === '=~' || row.operator === '!~';
245
- return (_jsxs("div", { className: `flex items-center gap-3 ${maxWidthInPixels} w-full flex-wrap`, id: "simple-matchers", ...testId('SIMPLE_MATCHERS_CONTAINER'), children: [visibleRows.map((row, index) => (_jsxs("div", { className: "flex items-center", ...testId('SIMPLE_MATCHER_ROW'), children: [_jsx(Select, { items: labelNameOptions, 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('LABEL_NAME_SELECT') }), _jsx(Select, { items: operatorOptions, onSelection: value => handleUpdateRow(index, 'operator', value), selectedKey: row.operator, className: "rounded-none ring-0 focus:ring-0 outline-none", ...testId('OPERATOR_SELECT') }), _jsx(Select, { items: transformLabelsForSelect(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-[300px]', {
245
+ 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: labelNameOptions, 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) }), _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.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-[300px]', {
246
246
  'w-[300px]': isRowRegex(row),
247
- }), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row), ...testId('LABEL_VALUE_SELECT') }), _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('REMOVE_MATCHER_BUTTON'), children: _jsx(Icon, { icon: "carbon:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }, index))), queryRows.length > 3 && (_jsx("button", { onClick: () => setShowAll(!showAll), 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 ? 'SHOW_LESS_BUTTON' : '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('ADD_MATCHER_BUTTON'), children: _jsx(Icon, { icon: "material-symbols:add", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }));
247
+ }), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row), ...testId(TEST_IDS.LABEL_VALUE_SELECT) }), _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 && (_jsx("button", { onClick: () => setShowAll(!showAll), 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" }) })] }));
248
248
  };
249
249
  export default function SimpleMathersWithProvider(props) {
250
250
  const labelNameFromMatchers = useMemo(() => {
@@ -15,7 +15,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
17
  import { useGrpcMetadata } from '@parca/components';
18
- import { testId } from '@parca/test-utils';
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
21
  const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatchersString, start, end, currentQuery, }) => {
@@ -112,6 +112,6 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
112
112
  element: { active: _jsx(_Fragment, { children: value }), expanded: _jsx(_Fragment, { children: value }) },
113
113
  }));
114
114
  }, []);
115
- return (_jsx("div", { className: "flex flex-wrap gap-2", ...testId('VIEW_MATCHERS_CONTAINER'), children: labelNames.map(labelName => (_jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "relative border shadow-sm px-4 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 text-sm flex gap-2 items-center justify-between bg-gray-100 dark:bg-gray-700 rounded-l-md border-gray-300 dark:border-gray-600", children: labelName }), _jsx(CustomSelect, { searchable: true, placeholder: "Select value", items: transformValuesForSelect(labelValuesMap[labelName] ?? []), onSelection: (value) => handleSelection(labelName, value), selectedKey: selectionsRef.current[labelName] ?? undefined, className: cx('rounded-l-none border-l-0', selectionsRef.current[labelName] != null && 'border-r-0 rounded-r-none'), loading: isLoading[labelName] ?? false }), selectionsRef.current[labelName] != null && (_jsx("button", { onClick: () => handleReset(labelName), className: "p-2 border-gray-200 bg-white dark:bg-gray-900 dark:border-gray-600 border rounded-r-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", "aria-label": `Reset ${labelName} selection`, children: _jsx(Icon, { icon: "mdi:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) }))] }, labelName))) }));
115
+ return (_jsx("div", { className: "flex flex-wrap gap-2", ...testId(TEST_IDS.VIEW_MATCHERS_CONTAINER), children: labelNames.map(labelName => (_jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "relative border shadow-sm px-4 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 text-sm flex gap-2 items-center justify-between bg-gray-100 dark:bg-gray-700 rounded-l-md border-gray-300 dark:border-gray-600", children: labelName }), _jsx(CustomSelect, { searchable: true, placeholder: "Select value", items: transformValuesForSelect(labelValuesMap[labelName] ?? []), onSelection: (value) => handleSelection(labelName, value), selectedKey: selectionsRef.current[labelName] ?? undefined, className: cx('rounded-l-none border-l-0', selectionsRef.current[labelName] != null && 'border-r-0 rounded-r-none'), loading: isLoading[labelName] ?? false }), selectionsRef.current[labelName] != null && (_jsx("button", { onClick: () => handleReset(labelName), className: "p-2 border-gray-200 bg-white dark:bg-gray-900 dark:border-gray-600 border rounded-r-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", "aria-label": `Reset ${labelName} selection`, children: _jsx(Icon, { icon: "mdi:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) }))] }, labelName))) }));
116
116
  };
117
117
  export default ViewMatchers;
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.19.53",
3
+ "version": "0.19.55",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@floating-ui/react": "^0.27.12",
7
7
  "@headlessui/react": "^1.7.19",
8
8
  "@iconify/react": "^4.0.0",
9
9
  "@parca/client": "0.17.5",
10
- "@parca/components": "0.16.370",
10
+ "@parca/components": "0.16.372",
11
11
  "@parca/dynamicsize": "0.16.67",
12
12
  "@parca/hooks": "0.0.102",
13
13
  "@parca/icons": "0.16.74",
14
14
  "@parca/parser": "0.16.81",
15
15
  "@parca/store": "0.16.186",
16
- "@parca/test-utils": "0.0.12",
16
+ "@parca/test-utils": "0.0.14",
17
17
  "@parca/utilities": "0.0.109",
18
18
  "@popperjs/core": "^2.11.8",
19
19
  "@protobuf-ts/runtime-rpc": "^2.5.0",
@@ -79,5 +79,5 @@
79
79
  "access": "public",
80
80
  "registry": "https://registry.npmjs.org/"
81
81
  },
82
- "gitHead": "364f74584252524568010bb27636fdb87fe7aee7"
82
+ "gitHead": "345d9fd77a27f537e110fd04060ae55c7ebb9266"
83
83
  }
@@ -20,7 +20,7 @@ import TextareaAutosize from 'react-textarea-autosize';
20
20
  import {LabelsRequest, LabelsResponse, QueryServiceClient, ValuesRequest} from '@parca/client';
21
21
  import {useGrpcMetadata} from '@parca/components';
22
22
  import {Query} from '@parca/parser';
23
- import {testId} from '@parca/test-utils';
23
+ import {TEST_IDS, testId} from '@parca/test-utils';
24
24
  import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
25
25
 
26
26
  import {UtilizationLabels} from '../ProfileSelector';
@@ -292,7 +292,7 @@ const MatchersInput = ({
292
292
  return (
293
293
  <div
294
294
  className="w-full min-w-[300px] flex-1 font-mono relative"
295
- {...testId('MATCHERS_INPUT_CONTAINER')}
295
+ {...testId(TEST_IDS.MATCHERS_INPUT_CONTAINER)}
296
296
  >
297
297
  <TextareaAutosize
298
298
  ref={inputRef}
@@ -308,7 +308,7 @@ const MatchersInput = ({
308
308
  onChange={onChange}
309
309
  value={value}
310
310
  onBlur={unfocus}
311
- {...testId('MATCHERS_TEXTAREA')}
311
+ {...testId(TEST_IDS.MATCHERS_TEXTAREA)}
312
312
  onFocus={focus}
313
313
  disabled={profileSelected} // Disable input if no profile has been selected
314
314
  title={
@@ -15,7 +15,7 @@ import {Icon} from '@iconify/react';
15
15
  import {Item, Menu, Submenu} from 'react-contexify';
16
16
 
17
17
  import {useParcaContext} from '@parca/components';
18
- import {testId} from '@parca/test-utils';
18
+ import {TEST_IDS, testId} from '@parca/test-utils';
19
19
 
20
20
  import {Series, SeriesPoint} from '../';
21
21
 
@@ -115,7 +115,7 @@ const MetricsContextMenu = ({
115
115
  id={menuId}
116
116
  onVisibilityChange={trackVisibility}
117
117
  theme={isDarkMode ? 'dark' : ''}
118
- {...testId('METRICS_GRAPH_CONTEXT_MENU')}
118
+ {...testId(TEST_IDS.METRICS_GRAPH_CONTEXT_MENU)}
119
119
  >
120
120
  {menuItems.map(renderMenuItem)}
121
121
  </Menu>
@@ -15,7 +15,7 @@ import {useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {usePopper} from 'react-popper';
17
17
 
18
- import {testId} from '@parca/test-utils';
18
+ import {TEST_IDS, testId} from '@parca/test-utils';
19
19
 
20
20
  interface VirtualElement {
21
21
  getBoundingClientRect: () => DOMRect;
@@ -113,7 +113,7 @@ const MetricsTooltip = ({x, y, contextElement, content}: Props): JSX.Element =>
113
113
  ref={setPopperElement}
114
114
  style={styles.popper}
115
115
  {...attributes.popper}
116
- {...testId('METRICS_GRAPH_TOOLTIP')}
116
+ {...testId(TEST_IDS.METRICS_GRAPH_TOOLTIP)}
117
117
  className="z-50"
118
118
  >
119
119
  <div className="flex max-w-lg">
@@ -19,7 +19,7 @@ import throttle from 'lodash.throttle';
19
19
  import {useContextMenu} from 'react-contexify';
20
20
 
21
21
  import {DateTimeRange, useParcaContext} from '@parca/components';
22
- import {testId} from '@parca/test-utils';
22
+ import {TEST_IDS, testId} from '@parca/test-utils';
23
23
  import {formatDate, formatForTimespan, getPrecision, valueFormatter} from '@parca/utilities';
24
24
 
25
25
  import MetricsCircle from '../MetricsCircle';
@@ -84,7 +84,7 @@ const MetricsGraph = ({
84
84
  return (
85
85
  <div
86
86
  className="relative"
87
- {...testId('METRICS_GRAPH')}
87
+ {...testId(TEST_IDS.METRICS_GRAPH)}
88
88
  onClick={() => isInfoPanelOpen && setIsInfoPanelOpen(false)}
89
89
  >
90
90
  <div className="absolute right-0 top-0">
@@ -16,7 +16,7 @@ import {useState} from 'react';
16
16
  import {QueryServiceClient} from '@parca/client';
17
17
  import {useURLState} from '@parca/components';
18
18
  import {Query} from '@parca/parser';
19
- import {testId} from '@parca/test-utils';
19
+ import {TEST_IDS, testId} from '@parca/test-utils';
20
20
  import type {NavigateFunction} from '@parca/utilities';
21
21
 
22
22
  import {ProfileDiffSource, ProfileSelection, ProfileViewWithData} from '..';
@@ -64,9 +64,12 @@ const ProfileExplorerCompare = ({
64
64
  const [compareAbsolute] = useURLState('compare_absolute');
65
65
 
66
66
  return (
67
- <div {...testId('COMPARE_CONTAINER')}>
67
+ <div {...testId(TEST_IDS.COMPARE_CONTAINER)}>
68
68
  <div className="flex justify-between gap-2 relative mb-2">
69
- <div className="flex-column flex-1 p-2 shadow-md rounded-md" {...testId('COMPARE_SIDE_A')}>
69
+ <div
70
+ className="flex-column flex-1 p-2 shadow-md rounded-md"
71
+ {...testId(TEST_IDS.COMPARE_SIDE_A)}
72
+ >
70
73
  <ProfileSelector
71
74
  queryClient={queryClient}
72
75
  querySelection={queryA}
@@ -82,7 +85,10 @@ const ProfileExplorerCompare = ({
82
85
  setDisplayHideMetricsGraphButton={setShowMetricsGraph}
83
86
  />
84
87
  </div>
85
- <div className="flex-column flex-1 p-2 shadow-md rounded-md" {...testId('COMPARE_SIDE_B')}>
88
+ <div
89
+ className="flex-column flex-1 p-2 shadow-md rounded-md"
90
+ {...testId(TEST_IDS.COMPARE_SIDE_B)}
91
+ >
86
92
  <ProfileSelector
87
93
  queryClient={queryClient}
88
94
  querySelection={queryB}
@@ -101,7 +107,7 @@ const ProfileExplorerCompare = ({
101
107
  </div>
102
108
  <div className="grid grid-cols-1">
103
109
  {profileA != null && profileB != null ? (
104
- <div {...testId('COMPARE_PROFILE_VIEW')}>
110
+ <div {...testId(TEST_IDS.COMPARE_PROFILE_VIEW)}>
105
111
  <ProfileViewWithData
106
112
  queryClient={queryClient}
107
113
  profileSource={
@@ -25,7 +25,7 @@ import {
25
25
  useURLState,
26
26
  } from '@parca/components';
27
27
  import {ProfileType} from '@parca/parser';
28
- import {testId} from '@parca/test-utils';
28
+ import {TEST_IDS, testId} from '@parca/test-utils';
29
29
  import {capitalizeOnlyFirstLetter, divide} from '@parca/utilities';
30
30
 
31
31
  import {MergedProfileSource, ProfileSource} from '../ProfileSource';
@@ -396,7 +396,7 @@ const ProfileFlameGraph = function ProfileFlameGraphNonMemo({
396
396
  <div
397
397
  className={cx(!isInSandwichView ? 'min-h-48' : '')}
398
398
  id="h-flame-graph"
399
- {...testId('FLAMEGRAPH_CONTAINER')}
399
+ {...testId(TEST_IDS.FLAMEGRAPH_CONTAINER)}
400
400
  >
401
401
  <>{flameGraph}</>
402
402
  </div>
@@ -29,7 +29,7 @@ import {
29
29
  useParcaContext,
30
30
  } from '@parca/components';
31
31
  import {Query} from '@parca/parser';
32
- import {testId} from '@parca/test-utils';
32
+ import {TEST_IDS, testId} from '@parca/test-utils';
33
33
  import {capitalizeOnlyFirstLetter, formatDate, timePattern, valueFormatter} from '@parca/utilities';
34
34
 
35
35
  import {MergedProfileSelection, ProfileSelection} from '..';
@@ -491,7 +491,7 @@ const ProfileMetricsGraph = ({
491
491
  <div
492
492
  key={name}
493
493
  className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
494
- {...testId('TOOLTIP_LABEL')}
494
+ {...testId(TEST_IDS.TOOLTIP_LABEL)}
495
495
  >
496
496
  <TextWithTooltip
497
497
  text={`${name.replace('attributes.', '')}="${
@@ -513,7 +513,7 @@ const ProfileMetricsGraph = ({
513
513
  <div
514
514
  key={name}
515
515
  className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
516
- {...testId('TOOLTIP_LABEL')}
516
+ {...testId(TEST_IDS.TOOLTIP_LABEL)}
517
517
  >
518
518
  <TextWithTooltip
519
519
  text={`${name.replace('attributes.', '')}="${
@@ -534,7 +534,7 @@ const ProfileMetricsGraph = ({
534
534
  <div
535
535
  key={label.name}
536
536
  className="mr-3 inline-block rounded-lg bg-gray-200 px-2 py-1 text-xs font-bold text-gray-700 dark:bg-gray-700 dark:text-gray-400"
537
- {...testId('TOOLTIP_LABEL')}
537
+ {...testId(TEST_IDS.TOOLTIP_LABEL)}
538
538
  >
539
539
  <TextWithTooltip
540
540
  text={`${label.name}="${label.value}"`}
@@ -18,7 +18,7 @@ import Select, {type SelectInstance} from 'react-select';
18
18
  import {ProfileTypesResponse, QueryServiceClient} from '@parca/client';
19
19
  import {Button, DateTimeRange, DateTimeRangePicker, useParcaContext} from '@parca/components';
20
20
  import {ProfileType, Query} from '@parca/parser';
21
- import {testId} from '@parca/test-utils';
21
+ import {TEST_IDS, testId} from '@parca/test-utils';
22
22
 
23
23
  import MatchersInput from '../MatchersInput';
24
24
  import ProfileTypeSelector from '../ProfileTypeSelector';
@@ -97,11 +97,11 @@ export function QueryControls({
97
97
  return (
98
98
  <div
99
99
  className="flex w-full flex-wrap items-start gap-2"
100
- {...testId('QUERY_CONTROLS_CONTAINER')}
100
+ {...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER)}
101
101
  >
102
102
  {showProfileTypeSelector && (
103
103
  <div>
104
- <label className="text-xs" {...testId('PROFILE_TYPE_LABEL')}>
104
+ <label className="text-xs" {...testId(TEST_IDS.PROFILE_TYPE_LABEL)}>
105
105
  Profile type
106
106
  </label>
107
107
  <ProfileTypeSelector
@@ -118,11 +118,11 @@ export function QueryControls({
118
118
  <div
119
119
  className="w-full flex-1 flex flex-col gap-1 mt-auto"
120
120
  ref={queryBrowserRef}
121
- {...testId('QUERY_BROWSER_CONTAINER')}
121
+ {...testId(TEST_IDS.QUERY_BROWSER_CONTAINER)}
122
122
  >
123
123
  <div className="flex items-center justify-between">
124
124
  <div className="flex items-center gap-3">
125
- <label className="text-xs" {...testId('QUERY_LABEL')}>
125
+ <label className="text-xs" {...testId(TEST_IDS.QUERY_LABEL)}>
126
126
  Query
127
127
  </label>
128
128
  {viewComponent?.disableExplorativeQuerying !== true && (
@@ -136,7 +136,7 @@ export function QueryControls({
136
136
  className={`${
137
137
  advancedModeForQueryBrowser ? 'bg-indigo-600' : 'bg-gray-400 dark:bg-gray-800'
138
138
  } 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`}
139
- {...testId('ADVANCED_MODE_SWITCH')}
139
+ {...testId(TEST_IDS.ADVANCED_MODE_SWITCH)}
140
140
  >
141
141
  <span className="sr-only">Use setting</span>
142
142
  <span
@@ -146,7 +146,7 @@ export function QueryControls({
146
146
  } pointer-events-none inline-block h-[16px] w-[16px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out`}
147
147
  />
148
148
  </Switch>
149
- <label className="text-xs" {...testId('QUERY_MODE_LABEL')}>
149
+ <label className="text-xs" {...testId(TEST_IDS.QUERY_MODE_LABEL)}>
150
150
  Advanced Mode
151
151
  </label>
152
152
  </>
@@ -194,15 +194,15 @@ export function QueryControls({
194
194
  </div>
195
195
 
196
196
  {showSumBySelector && (
197
- <div {...testId('SUM_BY_CONTAINER')}>
197
+ <div {...testId(TEST_IDS.SUM_BY_CONTAINER)}>
198
198
  <div className="mb-0.5 mt-1.5 flex items-center justify-between">
199
- <label className="text-xs" {...testId('SUM_BY_LABEL')}>
199
+ <label className="text-xs" {...testId(TEST_IDS.SUM_BY_LABEL)}>
200
200
  Sum by
201
201
  </label>
202
202
  </div>
203
203
  <Select<SelectOption, true>
204
204
  id="h-sum-by-selector"
205
- data-testid={testId('SUM_BY_SELECT')['data-testid']}
205
+ data-testid={testId(TEST_IDS.SUM_BY_SELECT)['data-testid']}
206
206
  defaultValue={[]}
207
207
  isMulti
208
208
  isClearable={false}
@@ -250,11 +250,11 @@ export function QueryControls({
250
250
  onRangeSelection={setTimeRangeSelection}
251
251
  range={timeRangeSelection}
252
252
  timezone={timezone}
253
- {...testId('DATE_TIME_RANGE_PICKER')}
253
+ {...testId(TEST_IDS.DATE_TIME_RANGE_PICKER)}
254
254
  />
255
255
 
256
256
  <div>
257
- <label className="text-xs" {...testId('SEARCH_BUTTON_LABEL')}>
257
+ <label className="text-xs" {...testId(TEST_IDS.SEARCH_BUTTON_LABEL)}>
258
258
  &nbsp;
259
259
  </label>
260
260
  <Button
@@ -264,7 +264,7 @@ export function QueryControls({
264
264
  setQueryExpression(true);
265
265
  }}
266
266
  id="h-matcher-search-button"
267
- {...testId('SEARCH_BUTTON')}
267
+ {...testId(TEST_IDS.SEARCH_BUTTON)}
268
268
  >
269
269
  Search
270
270
  </Button>
@@ -25,6 +25,7 @@ import {
25
25
  } from '@parca/components';
26
26
  import {CloseIcon} from '@parca/icons';
27
27
  import {Query} from '@parca/parser';
28
+ import {TEST_IDS, testId} from '@parca/test-utils';
28
29
  import {type NavigateFunction} from '@parca/utilities';
29
30
 
30
31
  import {ProfileSelection} from '..';
@@ -331,7 +332,11 @@ const ProfileSelector = ({
331
332
  />
332
333
  {comparing && (
333
334
  <div>
334
- <IconButton onClick={() => closeProfile()} icon={<CloseIcon />} />
335
+ <IconButton
336
+ onClick={() => closeProfile()}
337
+ icon={<CloseIcon />}
338
+ {...testId(TEST_IDS.COMPARE_CLOSE_BUTTON)}
339
+ />
335
340
  </div>
336
341
  )}
337
342
  </div>
@@ -17,7 +17,7 @@ import {RpcError} from '@protobuf-ts/runtime-rpc';
17
17
 
18
18
  import {ProfileType, ProfileTypesResponse} from '@parca/client';
19
19
  import {Select, type SelectElement} from '@parca/components';
20
- import {testId} from '@parca/test-utils';
20
+ import {TEST_IDS, testId} from '@parca/test-utils';
21
21
 
22
22
  interface WellKnownProfile {
23
23
  name: string;
@@ -198,7 +198,7 @@ const ProfileTypeSelector = ({
198
198
  loading={loading}
199
199
  className="bg-white h-profile-type-dropdown"
200
200
  disabled={disabled}
201
- {...testId('PROFILE_TYPE_SELECTOR')}
201
+ {...testId(TEST_IDS.PROFILE_TYPE_SELECTOR)}
202
202
  />
203
203
  );
204
204
  };
@@ -17,7 +17,7 @@ import {Popover, Transition} from '@headlessui/react';
17
17
  import {usePopper} from 'react-popper';
18
18
 
19
19
  import {selectDarkMode, useAppSelector} from '@parca/store';
20
- import {testId} from '@parca/test-utils';
20
+ import {TEST_IDS, testId} from '@parca/test-utils';
21
21
  import {getIncreasedSpanColor, getNewSpanColor, getReducedSpanColor} from '@parca/utilities';
22
22
 
23
23
  const transparencyValues = [-100, -80, -60, -40, -20, 0, 20, 40, 60, 80, 100];
@@ -75,7 +75,7 @@ const DiffLegend = (): JSX.Element => {
75
75
  };
76
76
 
77
77
  return (
78
- <div className="mt-1 mb-2 hidden md:block" id="h-diff-legend" {...testId('DIFF_LEGEND')}>
78
+ <div className="mt-1 mb-2 hidden md:block" id="h-diff-legend" {...testId(TEST_IDS.DIFF_LEGEND)}>
79
79
  <div ref={setReferenceElement} className="flex items-center justify-center">
80
80
  <span>Better</span>
81
81
  <DiffLegendBar onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} />
@@ -13,7 +13,7 @@
13
13
 
14
14
  import Select from 'react-select';
15
15
 
16
- import {testId} from '@parca/test-utils';
16
+ import {TEST_IDS, testId} from '@parca/test-utils';
17
17
 
18
18
  import {FIELD_LABELS} from '../../../ProfileFlameGraph/FlameGraphArrow';
19
19
 
@@ -30,9 +30,9 @@ interface Props {
30
30
 
31
31
  const GroupByLabelsDropdown = ({labels, groupBy, setGroupByLabels}: Props): JSX.Element => {
32
32
  return (
33
- <div className="flex flex-col relative" {...testId('GROUP_BY_CONTAINER')}>
33
+ <div className="flex flex-col relative" {...testId(TEST_IDS.GROUP_BY_CONTAINER)}>
34
34
  <div className="flex items-center justify-between">
35
- <label className="text-sm" {...testId('GROUP_BY_LABEL')}>
35
+ <label className="text-sm" {...testId(TEST_IDS.GROUP_BY_LABEL)}>
36
36
  Group by
37
37
  </label>
38
38
  </div>
@@ -50,7 +50,7 @@ const GroupByLabelsDropdown = ({labels, groupBy, setGroupByLabels}: Props): JSX.
50
50
  MenuList: ({children, innerProps}) => (
51
51
  <div
52
52
  className="overflow-y-auto"
53
- {...testId('GROUP_BY_SELECT_FLYOUT')}
53
+ {...testId(TEST_IDS.GROUP_BY_SELECT_FLYOUT)}
54
54
  {...innerProps}
55
55
  // eslint-disable-next-line react/prop-types
56
56
  style={{...innerProps.style, height: '332px', maxHeight: '332px', fontSize: '14px'}}
@@ -17,7 +17,7 @@ import {Icon} from '@iconify/react';
17
17
  import cx from 'classnames';
18
18
 
19
19
  import {Button, Input, Select, type SelectItem} from '@parca/components';
20
- import {testId} from '@parca/test-utils';
20
+ import {TEST_IDS, testId} from '@parca/test-utils';
21
21
 
22
22
  import {useProfileViewContext} from '../../context/ProfileViewContext';
23
23
  import {getPresetByKey, getPresetsForProfileType, isPresetKey} from './filterPresets';
@@ -205,7 +205,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
205
205
  const filtersToRender = localFilters.length > 0 ? localFilters : appliedFilters ?? [];
206
206
 
207
207
  return (
208
- <div className="flex gap-2 w-full items-start" {...testId('PROFILE_FILTERS_CONTAINER')}>
208
+ <div className="flex gap-2 w-full items-start" {...testId(TEST_IDS.PROFILE_FILTERS_CONTAINER)}>
209
209
  <div className="flex-1 flex flex-wrap gap-2">
210
210
  {filtersToRender.map(filter => {
211
211
  const isNumberField = filter.field === 'address' || filter.field === 'line_number';
@@ -219,7 +219,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
219
219
  selectedKey={filter.type}
220
220
  placeholder="Select Filter"
221
221
  disabled={readOnly}
222
- {...testId('FILTER_TYPE_SELECT')}
222
+ {...testId(TEST_IDS.FILTER_TYPE_SELECT)}
223
223
  flyoutTestId="filter-type-select-flyout"
224
224
  onSelection={key => {
225
225
  // Check if this is a preset selection
@@ -269,7 +269,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
269
269
  items={fieldItems}
270
270
  selectedKey={filter.field ?? ''}
271
271
  disabled={readOnly}
272
- {...testId('FILTER_FIELD_SELECT')}
272
+ {...testId(TEST_IDS.FILTER_FIELD_SELECT)}
273
273
  flyoutTestId="filter-field-select-flyout"
274
274
  onSelection={key => {
275
275
  const newField = key as ProfileFilter['field'];
@@ -297,7 +297,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
297
297
  items={matchTypeItems}
298
298
  selectedKey={filter.matchType ?? ''}
299
299
  disabled={readOnly}
300
- {...testId('FILTER_MATCH_TYPE_SELECT')}
300
+ {...testId(TEST_IDS.FILTER_MATCH_TYPE_SELECT)}
301
301
  flyoutTestId="filter-match-type-select-flyout"
302
302
  onSelection={key =>
303
303
  updateFilter(filter.id, {matchType: key as ProfileFilter['matchType']})
@@ -316,7 +316,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
316
316
  onChange={e => updateFilter(filter.id, {value: e.target.value})}
317
317
  onKeyDown={handleKeyDown}
318
318
  className="rounded-none w-36 text-sm focus:outline-1"
319
- {...testId('FILTER_VALUE_INPUT')}
319
+ {...testId(TEST_IDS.FILTER_VALUE_INPUT)}
320
320
  />
321
321
  </>
322
322
  )}
@@ -324,7 +324,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
324
324
  {!readOnly && (
325
325
  <Button
326
326
  variant="neutral"
327
- {...testId('FILTER_REMOVE_BUTTON')}
327
+ {...testId(TEST_IDS.FILTER_REMOVE_BUTTON)}
328
328
  onClick={() => {
329
329
  // If we're displaying local filters and this is the last one, reset everything
330
330
  if (localFilters.length > 0 && localFilters.length === 1) {
@@ -358,7 +358,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
358
358
  variant="neutral"
359
359
  onClick={addFilter}
360
360
  className="p-3 h-[38px]"
361
- {...testId('ADD_FILTER_BUTTON')}
361
+ {...testId(TEST_IDS.ADD_FILTER_BUTTON)}
362
362
  >
363
363
  <Icon icon="mdi:filter-plus-outline" className="h-4 w-4" />
364
364
  </Button>
@@ -369,7 +369,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
369
369
  variant="neutral"
370
370
  onClick={addFilter}
371
371
  className="flex items-center gap-2"
372
- {...testId('ADD_FILTER_BUTTON')}
372
+ {...testId(TEST_IDS.ADD_FILTER_BUTTON)}
373
373
  >
374
374
  <Icon icon="mdi:filter-outline" className="h-4 w-4" />
375
375
  <span>Filter</span>
@@ -383,7 +383,7 @@ const ProfileFilters = ({readOnly = false}: ProfileFiltersProps = {}): JSX.Eleme
383
383
  onClick={onApplyFilters}
384
384
  disabled={!hasUnsavedChanges || !localFilters.some(isFilterComplete)}
385
385
  className={cx('flex items-center gap-2 sticky top-0')}
386
- {...testId('APPLY_FILTERS_BUTTON')}
386
+ {...testId(TEST_IDS.APPLY_FILTERS_BUTTON)}
387
387
  >
388
388
  <span>Apply</span>
389
389
  </Button>
@@ -18,7 +18,7 @@ import {Icon} from '@iconify/react';
18
18
  import {QueryServiceClient} from '@parca/client';
19
19
  import {Button} from '@parca/components';
20
20
  import {ProfileType} from '@parca/parser';
21
- import {testId} from '@parca/test-utils';
21
+ import {TEST_IDS, testId} from '@parca/test-utils';
22
22
 
23
23
  import {CurrentPathFrame} from '../../../ProfileFlameGraph/FlameGraphArrow/utils';
24
24
  import {ProfileSource} from '../../../ProfileSource';
@@ -88,7 +88,7 @@ export const FlameGraphToolbar: FC<FlameGraphToolbarProps> = ({curPath, setNewCu
88
88
  onClick={() => setNewCurPath([])}
89
89
  disabled={curPath.length === 0}
90
90
  id="h-reset-graph"
91
- {...testId('FLAMEGRAPH_RESET_BUTTON')}
91
+ {...testId(TEST_IDS.FLAMEGRAPH_RESET_BUTTON)}
92
92
  >
93
93
  Reset graph
94
94
  <Icon icon="system-uicons:reset" width={20} />
@@ -20,7 +20,7 @@ import cx from 'classnames';
20
20
  import {QueryServiceClient} from '@parca/client';
21
21
  import {useGrpcMetadata} from '@parca/components';
22
22
  import {Query} from '@parca/parser';
23
- import {testId} from '@parca/test-utils';
23
+ import {TEST_IDS, testId} from '@parca/test-utils';
24
24
  import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
25
25
 
26
26
  import {LabelProvider, useLabels} from '../contexts/SimpleMatchersLabelContext';
@@ -357,10 +357,10 @@ const SimpleMatchers = ({
357
357
  <div
358
358
  className={`flex items-center gap-3 ${maxWidthInPixels} w-full flex-wrap`}
359
359
  id="simple-matchers"
360
- {...testId('SIMPLE_MATCHERS_CONTAINER')}
360
+ {...testId(TEST_IDS.SIMPLE_MATCHERS_CONTAINER)}
361
361
  >
362
362
  {visibleRows.map((row, index) => (
363
- <div key={index} className="flex items-center" {...testId('SIMPLE_MATCHER_ROW')}>
363
+ <div key={index} className="flex items-center" {...testId(TEST_IDS.SIMPLE_MATCHER_ROW)}>
364
364
  <Select
365
365
  items={labelNameOptions}
366
366
  onSelection={value => handleUpdateRow(index, 'labelName', value)}
@@ -369,14 +369,14 @@ const SimpleMatchers = ({
369
369
  className="rounded-tr-none rounded-br-none ring-0 focus:ring-0 outline-none"
370
370
  loading={labelNamesLoading}
371
371
  searchable={true}
372
- {...testId('LABEL_NAME_SELECT')}
372
+ {...testId(TEST_IDS.LABEL_NAME_SELECT)}
373
373
  />
374
374
  <Select
375
375
  items={operatorOptions}
376
376
  onSelection={value => handleUpdateRow(index, 'operator', value)}
377
377
  selectedKey={row.operator}
378
378
  className="rounded-none ring-0 focus:ring-0 outline-none"
379
- {...testId('OPERATOR_SELECT')}
379
+ {...testId(TEST_IDS.OPERATOR_SELECT)}
380
380
  />
381
381
  <Select
382
382
  items={transformLabelsForSelect(row.labelValues)}
@@ -392,14 +392,14 @@ const SimpleMatchers = ({
392
392
  loading={row.isLoading}
393
393
  onButtonClick={() => handleLabelValueClick(index)}
394
394
  editable={isRowRegex(row)}
395
- {...testId('LABEL_VALUE_SELECT')}
395
+ {...testId(TEST_IDS.LABEL_VALUE_SELECT)}
396
396
  />
397
397
  <button
398
398
  onClick={() => removeRow(index)}
399
399
  className={cx(
400
400
  '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'
401
401
  )}
402
- {...testId('REMOVE_MATCHER_BUTTON')}
402
+ {...testId(TEST_IDS.REMOVE_MATCHER_BUTTON)}
403
403
  >
404
404
  <Icon icon="carbon:close" className="h-5 w-5 text-gray-400" aria-hidden="true" />
405
405
  </button>
@@ -410,7 +410,7 @@ const SimpleMatchers = ({
410
410
  <button
411
411
  onClick={() => setShowAll(!showAll)}
412
412
  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"
413
- {...testId(showAll ? 'SHOW_LESS_BUTTON' : 'SHOW_MORE_BUTTON')}
413
+ {...testId(showAll ? TEST_IDS.SHOW_LESS_BUTTON : TEST_IDS.SHOW_MORE_BUTTON)}
414
414
  >
415
415
  {showAll ? 'Show less' : `Show ${hiddenRowsCount} more`}
416
416
  </button>
@@ -419,7 +419,7 @@ const SimpleMatchers = ({
419
419
  <button
420
420
  onClick={addNewRow}
421
421
  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"
422
- {...testId('ADD_MATCHER_BUTTON')}
422
+ {...testId(TEST_IDS.ADD_MATCHER_BUTTON)}
423
423
  >
424
424
  <Icon icon="material-symbols:add" className="h-5 w-5 text-gray-400" aria-hidden="true" />
425
425
  </button>
@@ -19,7 +19,7 @@ import cx from 'classnames';
19
19
  import {QueryServiceClient} from '@parca/client';
20
20
  import {useGrpcMetadata} from '@parca/components';
21
21
  import {Query} from '@parca/parser';
22
- import {testId} from '@parca/test-utils';
22
+ import {TEST_IDS, testId} from '@parca/test-utils';
23
23
  import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
24
24
 
25
25
  import CustomSelect, {SelectItem} from '../SimpleMatchers/Select';
@@ -173,7 +173,7 @@ const ViewMatchers: React.FC<Props> = ({
173
173
  }, []);
174
174
 
175
175
  return (
176
- <div className="flex flex-wrap gap-2" {...testId('VIEW_MATCHERS_CONTAINER')}>
176
+ <div className="flex flex-wrap gap-2" {...testId(TEST_IDS.VIEW_MATCHERS_CONTAINER)}>
177
177
  {labelNames.map(labelName => (
178
178
  <div key={labelName} className="flex items-center">
179
179
  <div className="relative border shadow-sm px-4 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 text-sm flex gap-2 items-center justify-between bg-gray-100 dark:bg-gray-700 rounded-l-md border-gray-300 dark:border-gray-600">