Skip to content

Commit

Permalink
Merge branch 'main' into ifrost/refactor-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
ifrost committed Feb 4, 2025
2 parents 472bd77 + 25095c6 commit a3e0c1d
Show file tree
Hide file tree
Showing 67 changed files with 213 additions and 15 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion e2e/tests/labels-view/labels.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ test.describe('Labels view', () => {
test('Panel type switcher', async ({ exploreProfilesPage }) => {
await exploreProfilesPage.assertNoSpinner();

for (const panelType of ['Totals', 'Histograms']) {
for (const panelType of ['Totals', 'Maxima', 'Histograms']) {
await exploreProfilesPage.selectPanelType(panelType);

await expect(exploreProfilesPage.getGroupByContainer()).toHaveScreenshot({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getSceneVariableValue } from '../../helpers/getSceneVariableValue';
import { vizPanelBuilder } from '../../helpers/vizPanelBuilder';
import { SceneLabelValuesBarGauge } from '../SceneLabelValuesBarGauge';
import { SceneLabelValuesHistogram } from '../SceneLabelValuesHistogram';
import { SceneLabelValuesTable } from '../SceneLabelValuesTable';
import { SceneLabelValuesTimeseries } from '../SceneLabelValuesTimeseries/SceneLabelValuesTimeseries';
import { SceneEmptyState } from './components/SceneEmptyState/SceneEmptyState';
import { SceneErrorState } from './components/SceneErrorState/SceneErrorState';
Expand Down Expand Up @@ -311,7 +312,9 @@ export class SceneByVariableRepeaterGrid extends SceneObjectBase<SceneByVariable
});
}

setupHideNoData(vizPanel: SceneLabelValuesTimeseries | SceneLabelValuesBarGauge | SceneLabelValuesHistogram) {
setupHideNoData(
vizPanel: SceneLabelValuesTimeseries | SceneLabelValuesBarGauge | SceneLabelValuesHistogram | SceneLabelValuesTable
) {
const sub = vizPanel.subscribeToEvent(EventTimeseriesDataReceived, (event) => {
if (event.payload.series?.length) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import React from 'react';
export enum PanelType {
TIMESERIES = 'time-series',
BARGAUGE = 'bar-gauge',
TABLE = 'table',
HISTOGRAM = 'histogram',
}

Expand All @@ -26,6 +27,7 @@ export class ScenePanelTypeSwitcher extends SceneObjectBase<ScenePanelTypeSwitch
static OPTIONS = [
{ label: 'Time series', value: PanelType.TIMESERIES, icon: 'heart-rate' },
{ label: 'Totals', value: PanelType.BARGAUGE, icon: 'align-left' },
{ label: 'Maxima', value: PanelType.TABLE, icon: 'angle-double-up' },
{ label: 'Histograms', value: PanelType.HISTOGRAM, icon: 'graph-bar' },
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,19 @@ export const addStats = () => (source: Observable<DataFrame[]>) =>
map((data: DataFrame[]) => {
const totalSeriesCount = data?.length;

// TODO: in case of a groupBy query, find a way to always add a rank to each label value (based on allValuesSum) so that we can use it as startColorIndex to
// always display each series consistently in the same color regardless of it's timseries, bar gauges with sums, or tables with maxima
return data?.map((d) => {
let maxValue = Number.NEGATIVE_INFINITY;

const allValuesSum = d.fields
?.find((field) => field.type === 'number')
?.values.reduce((acc: number, value: number) => acc + value, 0);
?.values.reduce((acc: number, value: number) => {
if (value > maxValue) {
maxValue = value;
}
return acc + value;
}, 0);

return merge(d, {
meta: {
Expand All @@ -29,6 +38,10 @@ export const addStats = () => (source: Observable<DataFrame[]>) =>
displayName: 'allValuesSum',
value: allValuesSum,
},
{
displayName: 'maxValue',
value: maxValue,
},
],
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DataFrame, FieldMatcherID, getValueFormat, LoadingState } from '@grafana/data';
import { DataFrame, FieldMatcherID, LoadingState } from '@grafana/data';
import {
PanelBuilders,
SceneComponentProps,
Expand All @@ -13,9 +13,9 @@ import { LegendDisplayMode, TooltipDisplayMode, VizLegendOptions } from '@grafan
import React from 'react';

import { EventTimeseriesDataReceived } from '../domain/events/EventTimeseriesDataReceived';
import { formatSingleSeriesDisplayName } from '../helpers/formatSingleSeriesDisplayName';
import { getColorByIndex } from '../helpers/getColorByIndex';
import { getSeriesLabelFieldName } from '../infrastructure/helpers/getSeriesLabelFieldName';
import { getSeriesStatsValue } from '../infrastructure/helpers/getSeriesStatsValue';
import { buildTimeSeriesQueryRunner } from '../infrastructure/timeseries/buildTimeSeriesQueryRunner';
import { addRefId, addStats } from './SceneByVariableRepeaterGrid/infrastructure/data-transformations';
import { GridItemData } from './SceneByVariableRepeaterGrid/types/GridItemData';
Expand Down Expand Up @@ -116,10 +116,7 @@ export class SceneLabelValuesHistogram extends SceneObjectBase<SceneLabelValuesH
let displayName = groupByLabel ? getSeriesLabelFieldName(metricField, groupByLabel) : metricField.name;

if (series.length === 1) {
const allValuesSum = getSeriesStatsValue(s, 'allValuesSum') || 0;
const formattedValue = getValueFormat(metricField.config.unit)(allValuesSum);

displayName = `total ${displayName} = ${formattedValue.text}${formattedValue.suffix}`;
displayName = formatSingleSeriesDisplayName(displayName, s);
}

return {
Expand Down
167 changes: 167 additions & 0 deletions src/pages/ProfilesExplorerView/components/SceneLabelValuesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { css } from '@emotion/css';
import { DataFrame, DataTransformerID, LoadingState } from '@grafana/data';
import {
PanelBuilders,
SceneComponentProps,
SceneDataTransformer,
sceneGraph,
SceneObjectBase,
SceneObjectState,
VizPanel,
VizPanelState,
} from '@grafana/scenes';
import { useStyles2 } from '@grafana/ui';
import { getProfileMetric, ProfileMetricId } from '@shared/infrastructure/profile-metrics/getProfileMetric';
import React from 'react';

import { EventTimeseriesDataReceived } from '../domain/events/EventTimeseriesDataReceived';
import { ProfileMetricVariable } from '../domain/variables/ProfileMetricVariable';
import { getColorByIndex } from '../helpers/getColorByIndex';
import { buildTimeSeriesQueryRunner } from '../infrastructure/timeseries/buildTimeSeriesQueryRunner';
import { GridItemData } from './SceneByVariableRepeaterGrid/types/GridItemData';

interface SceneLabelValuesTableState extends SceneObjectState {
body: VizPanel;
}

export class SceneLabelValuesTable extends SceneObjectBase<SceneLabelValuesTableState> {
constructor({
item,
headerActions,
}: {
item: GridItemData;
headerActions: (item: GridItemData) => VizPanelState['headerActions'];
}) {
super({
key: 'table-label-values',
body: PanelBuilders.table()
.setTitle(item.label)
.setData(
new SceneDataTransformer({
$data: buildTimeSeriesQueryRunner(item.queryRunnerParams),
transformations: [
{
id: DataTransformerID.reduce,
options: {
reducers: ['max'],
labelsToFields: true,
},
},
{
id: DataTransformerID.filterFieldsByName,
options: {
exclude: {
names: ['Field'],
},
},
},
{
id: DataTransformerID.renameByRegex,
options: {
regex: 'Max',
renamePattern: 'max',
},
},
{
id: DataTransformerID.sortBy,
options: {
sort: [
{
field: 'max',
desc: true,
},
],
},
},
],
})
)
.setHeaderActions(headerActions(item))
.build(),
});

this.addActivationHandler(this.onActivate.bind(this, item));
}

onActivate(item: GridItemData) {
const { body } = this.state;

const sub = (body.state.$data as SceneDataTransformer)!.subscribeToState((newState) => {
if (newState.data?.state !== LoadingState.Done) {
return;
}

const { series } = newState.data;

if (series?.length) {
body.setState(this.getConfig(item, series));
}

// we publish the event only after setting the new config so that the subscribers can modify it
this.publishEvent(new EventTimeseriesDataReceived({ series }), true);
});

return () => {
sub.unsubscribe();
};
}

getConfig(item: GridItemData, series: DataFrame[]) {
const cardinality = series[0].fields[0].values.length;

const profileMetricId = sceneGraph.findByKeyAndType(this, 'profileMetricId', ProfileMetricVariable).state
.value as ProfileMetricId;
const unitValue = getProfileMetric(profileMetricId).unit;

return {
title: cardinality > 1 ? `${item.label} (${cardinality})` : item.label,
fieldConfig: {
defaults: {
custom: {
filterable: true,
cellOptions: {},
},
},
overrides: [
{
matcher: {
id: 'byName',
options: 'max',
},
properties: [
{
id: 'unit',
value: unitValue,
},
{
id: 'custom.width',
value: 100,
},
],
},
],
},
};
}

static Component({ model }: SceneComponentProps<SceneLabelValuesTable>) {
const styles = useStyles2(getStyles); // eslint-disable-line react-hooks/rules-of-hooks
const { body } = model.useState();

return (
<span className={styles.container}>
<body.Component model={body} />
</span>
);
}
}

const getStyles = () => ({
// couldn't find a better way just by configuring the table panel :man_shrug:
container: css`
[data-testid='data-testid table body'] [role='row']:first-child {
color: ${getColorByIndex(3)};
font-weight: 500;
}
`,
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DataFrame, FieldMatcherID, getValueFormat, LoadingState } from '@grafana/data';
import { DataFrame, FieldMatcherID, LoadingState } from '@grafana/data';
import {
PanelBuilders,
SceneComponentProps,
Expand All @@ -17,9 +17,9 @@ import { isEqual, merge } from 'lodash';
import React from 'react';

import { EventTimeseriesDataReceived } from '../../domain/events/EventTimeseriesDataReceived';
import { formatSingleSeriesDisplayName } from '../../helpers/formatSingleSeriesDisplayName';
import { getColorByIndex } from '../../helpers/getColorByIndex';
import { getSeriesLabelFieldName } from '../../infrastructure/helpers/getSeriesLabelFieldName';
import { getSeriesStatsValue } from '../../infrastructure/helpers/getSeriesStatsValue';
import { LabelsDataSource } from '../../infrastructure/labels/LabelsDataSource';
import { buildTimeSeriesQueryRunner } from '../../infrastructure/timeseries/buildTimeSeriesQueryRunner';
import { addRefId, addStats } from '../SceneByVariableRepeaterGrid/infrastructure/data-transformations';
Expand Down Expand Up @@ -207,10 +207,7 @@ export class SceneLabelValuesTimeseries extends SceneObjectBase<SceneLabelValues
let displayName = groupByLabel ? getSeriesLabelFieldName(metricField, groupByLabel) : metricField.name;

if (series.length === 1) {
const allValuesSum = getSeriesStatsValue(s, 'allValuesSum') || 0;
const formattedValue = getValueFormat(metricField.config.unit)(allValuesSum);

displayName = `total ${displayName} = ${formattedValue.text}${formattedValue.suffix}`;
displayName = formatSingleSeriesDisplayName(displayName, s);
}

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DataFrame, getValueFormat } from '@grafana/data';

import { getSeriesStatsValue } from '../infrastructure/helpers/getSeriesStatsValue';

export function formatSingleSeriesDisplayName(label: string, s: DataFrame) {
const metricFieldUnit = s.fields[1].config.unit;

const allValuesSum = getSeriesStatsValue(s, 'allValuesSum') || 0;
const allValuesSumFormatted = getValueFormat(metricFieldUnit)(allValuesSum);

const maxValue = getSeriesStatsValue(s, 'maxValue') || 0;
const maxValueFormatted = getValueFormat(metricFieldUnit)(maxValue);

return `total ${label} = ${allValuesSumFormatted.text}${allValuesSumFormatted.suffix} / max = ${maxValueFormatted.text}${maxValueFormatted.suffix}`;
}
4 changes: 4 additions & 0 deletions src/pages/ProfilesExplorerView/helpers/vizPanelBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { PanelType } from '../components/SceneByVariableRepeaterGrid/components/ScenePanelTypeSwitcher';
import { SceneLabelValuesBarGauge } from '../components/SceneLabelValuesBarGauge';
import { SceneLabelValuesHistogram } from '../components/SceneLabelValuesHistogram';
import { SceneLabelValuesTable } from '../components/SceneLabelValuesTable';
import { SceneLabelValuesTimeseries } from '../components/SceneLabelValuesTimeseries/SceneLabelValuesTimeseries';

export function vizPanelBuilder(panelType: PanelType, options: any) {
switch (panelType) {
case PanelType.BARGAUGE:
return new SceneLabelValuesBarGauge(options);

case PanelType.TABLE:
return new SceneLabelValuesTable(options);

case PanelType.HISTOGRAM:
return new SceneLabelValuesHistogram(options);

Expand Down

0 comments on commit a3e0c1d

Please sign in to comment.