|
@@ -25,6 +25,7 @@ import {
|
|
|
import {metricDisplayTypeOptions} from 'sentry/utils/metrics/constants';
|
|
|
import {parseMRI} from 'sentry/utils/metrics/mri';
|
|
|
import type {
|
|
|
+ FocusedMetricsSeries,
|
|
|
MetricCorrelation,
|
|
|
MetricWidgetQueryParams,
|
|
|
} from 'sentry/utils/metrics/types';
|
|
@@ -37,6 +38,7 @@ import type {FocusAreaProps} from 'sentry/views/ddm/context';
|
|
|
import {createChartPalette} from 'sentry/views/ddm/metricsChartPalette';
|
|
|
import {QuerySymbol} from 'sentry/views/ddm/querySymbol';
|
|
|
import {SummaryTable} from 'sentry/views/ddm/summaryTable';
|
|
|
+import {getQueryWithFocusedSeries} from 'sentry/views/ddm/utils';
|
|
|
|
|
|
import {DDM_CHART_GROUP, MIN_WIDGET_WIDTH} from './constants';
|
|
|
|
|
@@ -64,12 +66,6 @@ export type Sample = {
|
|
|
transactionSpanId: string;
|
|
|
};
|
|
|
|
|
|
-const constructQueryString = (queryObject: Record<string, string>) => {
|
|
|
- return Object.entries(queryObject)
|
|
|
- .map(([key, value]) => `${key}:"${value}"`)
|
|
|
- .join(' ');
|
|
|
-};
|
|
|
-
|
|
|
export const MetricWidget = memo(
|
|
|
({
|
|
|
widget,
|
|
@@ -130,11 +126,14 @@ export const MetricWidget = memo(
|
|
|
onChange(index, {displayType: value});
|
|
|
};
|
|
|
|
|
|
+ const queryWithFocusedSeries = useMemo(
|
|
|
+ () => getQueryWithFocusedSeries(widget),
|
|
|
+ [widget]
|
|
|
+ );
|
|
|
+
|
|
|
const samplesQuery = useMetricSamples(metricsQuery.mri, {
|
|
|
...focusArea?.selection?.range,
|
|
|
- query: widget?.focusedSeries?.groupBy
|
|
|
- ? `${widget.query} ${constructQueryString(widget.focusedSeries.groupBy)}`.trim()
|
|
|
- : widget?.query,
|
|
|
+ query: queryWithFocusedSeries,
|
|
|
});
|
|
|
|
|
|
const samples = useMemo(() => {
|
|
@@ -272,26 +271,68 @@ const MetricWidgetBody = memo(
|
|
|
});
|
|
|
}, []);
|
|
|
|
|
|
- const toggleSeriesVisibility = useCallback(
|
|
|
- (series: MetricWidgetQueryParams['focusedSeries']) => {
|
|
|
- setHoveredSeries('');
|
|
|
- onChange?.({
|
|
|
- focusedSeries:
|
|
|
- focusedSeries?.seriesName === series?.seriesName ? undefined : series,
|
|
|
- });
|
|
|
- },
|
|
|
- [focusedSeries, onChange, setHoveredSeries]
|
|
|
- );
|
|
|
-
|
|
|
const chartSeries = useMemo(() => {
|
|
|
return timeseriesData
|
|
|
? getChartTimeseries(timeseriesData, {
|
|
|
getChartPalette,
|
|
|
mri,
|
|
|
- focusedSeries: focusedSeries?.seriesName,
|
|
|
+ focusedSeries:
|
|
|
+ focusedSeries && new Set(focusedSeries?.map(s => s.seriesName)),
|
|
|
})
|
|
|
: [];
|
|
|
- }, [timeseriesData, getChartPalette, mri, focusedSeries?.seriesName]);
|
|
|
+ }, [timeseriesData, getChartPalette, mri, focusedSeries]);
|
|
|
+
|
|
|
+ const toggleSeriesVisibility = useCallback(
|
|
|
+ (series: FocusedMetricsSeries) => {
|
|
|
+ setHoveredSeries('');
|
|
|
+
|
|
|
+ // The focused series array is not populated yet, so we can add all series except the one that was de-selected
|
|
|
+ if (!focusedSeries || focusedSeries.length === 0) {
|
|
|
+ onChange?.({
|
|
|
+ focusedSeries: chartSeries
|
|
|
+ .filter(s => s.seriesName !== series.seriesName)
|
|
|
+ .map(s => ({
|
|
|
+ seriesName: s.seriesName,
|
|
|
+ groupBy: s.groupBy,
|
|
|
+ })),
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const filteredSeries = focusedSeries.filter(
|
|
|
+ s => s.seriesName !== series.seriesName
|
|
|
+ );
|
|
|
+
|
|
|
+ if (filteredSeries.length === focusedSeries.length) {
|
|
|
+ // The series was not focused before so we can add it
|
|
|
+ filteredSeries.push(series);
|
|
|
+ }
|
|
|
+
|
|
|
+ onChange?.({
|
|
|
+ focusedSeries: filteredSeries,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ [chartSeries, focusedSeries, onChange, setHoveredSeries]
|
|
|
+ );
|
|
|
+
|
|
|
+ const setSeriesVisibility = useCallback(
|
|
|
+ (series: FocusedMetricsSeries) => {
|
|
|
+ setHoveredSeries('');
|
|
|
+ if (
|
|
|
+ focusedSeries?.length === 1 &&
|
|
|
+ focusedSeries[0].seriesName === series.seriesName
|
|
|
+ ) {
|
|
|
+ onChange?.({
|
|
|
+ focusedSeries: [],
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ onChange?.({
|
|
|
+ focusedSeries: [series],
|
|
|
+ });
|
|
|
+ },
|
|
|
+ [focusedSeries, onChange, setHoveredSeries]
|
|
|
+ );
|
|
|
|
|
|
const handleSortChange = useCallback(
|
|
|
newSort => {
|
|
@@ -345,8 +386,9 @@ const MetricWidgetBody = memo(
|
|
|
onSortChange={handleSortChange}
|
|
|
sort={sort}
|
|
|
operation={metricsQuery.op}
|
|
|
- onRowClick={toggleSeriesVisibility}
|
|
|
- setHoveredSeries={focusedSeries ? undefined : setHoveredSeries}
|
|
|
+ onRowClick={setSeriesVisibility}
|
|
|
+ onColorDotClick={toggleSeriesVisibility}
|
|
|
+ setHoveredSeries={setHoveredSeries}
|
|
|
/>
|
|
|
)}
|
|
|
</StyledMetricWidgetBody>
|
|
@@ -363,7 +405,7 @@ export function getChartTimeseries(
|
|
|
}: {
|
|
|
getChartPalette: (seriesNames: string[]) => Record<string, string>;
|
|
|
mri: MRI;
|
|
|
- focusedSeries?: string;
|
|
|
+ focusedSeries?: Set<string>;
|
|
|
}
|
|
|
) {
|
|
|
// this assumes that all series have the same unit
|
|
@@ -387,7 +429,7 @@ export function getChartTimeseries(
|
|
|
groupBy: item.groupBy,
|
|
|
unit,
|
|
|
color: chartPalette[item.name],
|
|
|
- hidden: focusedSeries && focusedSeries !== item.name,
|
|
|
+ hidden: focusedSeries && focusedSeries.size > 0 && !focusedSeries.has(item.name),
|
|
|
data: item.values.map((value, index) => ({
|
|
|
name: moment(data.intervals[index]).valueOf(),
|
|
|
value,
|