@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.
- package/CHANGELOG.md +8 -0
- package/dist/MatchersInput/index.js +3 -3
- package/dist/MetricsGraph/MetricsContextMenu/index.js +2 -2
- package/dist/MetricsGraph/MetricsTooltip/index.js +2 -2
- package/dist/MetricsGraph/index.js +2 -2
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts.map +1 -1
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +2 -2
- package/dist/ProfileFlameGraph/index.js +2 -2
- package/dist/ProfileMetricsGraph/index.js +3 -3
- package/dist/ProfileSelector/QueryControls.js +6 -6
- package/dist/ProfileSelector/index.d.ts.map +1 -1
- package/dist/ProfileSelector/index.js +2 -1
- package/dist/ProfileTypeSelector/index.js +2 -2
- package/dist/ProfileView/components/DiffLegend.js +2 -2
- package/dist/ProfileView/components/GroupByLabelsDropdown/index.js +3 -3
- package/dist/ProfileView/components/ProfileFilters/index.js +6 -6
- package/dist/ProfileView/components/Toolbars/index.js +2 -2
- package/dist/SimpleMatchers/index.js +3 -3
- package/dist/ViewMatchers/index.js +2 -2
- package/package.json +4 -4
- package/src/MatchersInput/index.tsx +3 -3
- package/src/MetricsGraph/MetricsContextMenu/index.tsx +2 -2
- package/src/MetricsGraph/MetricsTooltip/index.tsx +2 -2
- package/src/MetricsGraph/index.tsx +2 -2
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +11 -5
- package/src/ProfileFlameGraph/index.tsx +2 -2
- package/src/ProfileMetricsGraph/index.tsx +4 -4
- package/src/ProfileSelector/QueryControls.tsx +13 -13
- package/src/ProfileSelector/index.tsx +6 -1
- package/src/ProfileTypeSelector/index.tsx +2 -2
- package/src/ProfileView/components/DiffLegend.tsx +2 -2
- package/src/ProfileView/components/GroupByLabelsDropdown/index.tsx +4 -4
- package/src/ProfileView/components/ProfileFilters/index.tsx +10 -10
- package/src/ProfileView/components/Toolbars/index.tsx +2 -2
- package/src/SimpleMatchers/index.tsx +9 -9
- 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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAErF,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ProfileSelector/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAC,QAAQ,EAAE,cAAc,EAAuC,MAAM,OAAO,CAAC;AAErF,OAAO,EAAC,QAAQ,EAAC,MAAM,0BAA0B,CAAC;AAElD,OAAO,EAAC,oBAAoB,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAWvE,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,IAAI,CAAC;AASpC,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE;QACR,MAAM,EAAE,KAAK,CAAC;YACZ,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ,CAAC;IACF,OAAO,EAAE,KAAK,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,2BAA2B,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjE,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;IAClC,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,UAAU,oBAAqB,SAAQ,uBAAuB;IAC5D,WAAW,EAAE,kBAAkB,CAAC;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,gCAAgC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,KAAK,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,iBAAiB,EAAE,MAAM,CAAC;QAC1B,IAAI,EAAE,kBAAkB,EAAE,CAAC;KAC5B,CAAC,CAAC;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,yBAAyB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,eAAO,MAAM,eAAe,GAAI,QAAQ,kBAAkB,KAAG,mBAkB5D,CAAC;AAEF,QAAA,MAAM,eAAe,GAAI,mWAmBtB,oBAAoB,KAAG,GAAG,CAAC,OAiO7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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": "
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
67
|
+
<div {...testId(TEST_IDS.COMPARE_CONTAINER)}>
|
|
68
68
|
<div className="flex justify-between gap-2 relative mb-2">
|
|
69
|
-
<div
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
100
|
+
{...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER)}
|
|
101
101
|
>
|
|
102
102
|
{showProfileTypeSelector && (
|
|
103
103
|
<div>
|
|
104
|
-
<label className="text-xs" {...testId(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
253
|
+
{...testId(TEST_IDS.DATE_TIME_RANGE_PICKER)}
|
|
254
254
|
/>
|
|
255
255
|
|
|
256
256
|
<div>
|
|
257
|
-
<label className="text-xs" {...testId(
|
|
257
|
+
<label className="text-xs" {...testId(TEST_IDS.SEARCH_BUTTON_LABEL)}>
|
|
258
258
|
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
360
|
+
{...testId(TEST_IDS.SIMPLE_MATCHERS_CONTAINER)}
|
|
361
361
|
>
|
|
362
362
|
{visibleRows.map((row, index) => (
|
|
363
|
-
<div key={index} className="flex items-center" {...testId(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ?
|
|
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(
|
|
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(
|
|
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">
|