diff --git a/.betterer.results b/.betterer.results index d1d169aa96e..122f941bded 100644 --- a/.betterer.results +++ b/.betterer.results @@ -4319,10 +4319,6 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "11"], [0, 0, 0, "Do not use any type assertions.", "12"] ], - "public/app/features/trails/SelectMetricTrailView.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"] - ], "public/app/features/transformers/FilterByValueTransformer/ValueMatchers/BasicMatcherEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], diff --git a/public/app/features/trails/AddToFiltersGraphAction.tsx b/public/app/features/trails/ActionTabs/AddToFiltersGraphAction.tsx similarity index 96% rename from public/app/features/trails/AddToFiltersGraphAction.tsx rename to public/app/features/trails/ActionTabs/AddToFiltersGraphAction.tsx index d05806c4b94..c3403136493 100644 --- a/public/app/features/trails/AddToFiltersGraphAction.tsx +++ b/public/app/features/trails/ActionTabs/AddToFiltersGraphAction.tsx @@ -10,7 +10,7 @@ import { } from '@grafana/scenes'; import { Button } from '@grafana/ui'; -import { getMetricSceneFor } from './utils'; +import { getMetricSceneFor } from '../utils'; export interface AddToFiltersGraphActionState extends SceneObjectState { frame: DataFrame; diff --git a/public/app/features/trails/BreakdownScene.tsx b/public/app/features/trails/ActionTabs/BreakdownScene.tsx similarity index 84% rename from public/app/features/trails/BreakdownScene.tsx rename to public/app/features/trails/ActionTabs/BreakdownScene.tsx index ae42476288e..03fd9ebb3bd 100644 --- a/public/app/features/trails/BreakdownScene.tsx +++ b/public/app/features/trails/ActionTabs/BreakdownScene.tsx @@ -3,7 +3,6 @@ import React from 'react'; import { DataFrame, GrafanaTheme2, SelectableValue } from '@grafana/data'; import { - AdHocFiltersVariable, PanelBuilders, QueryVariable, SceneComponentProps, @@ -18,19 +17,20 @@ import { SceneObjectBase, SceneObjectState, SceneQueryRunner, - SceneVariableSet, } from '@grafana/scenes'; import { Button, Field, RadioButtonGroup, useStyles2 } from '@grafana/ui'; import { ALL_VARIABLE_VALUE } from 'app/features/variables/constants'; +import { getAutoQueriesForMetric } from '../AutomaticMetricQueries/AutoQueryEngine'; +import { AutoQueryDef } from '../AutomaticMetricQueries/types'; +import { MetricScene } from '../MetricScene'; +import { trailDS, VAR_GROUP_BY, VAR_GROUP_BY_EXP } from '../shared'; +import { getColorByIndex } from '../utils'; + import { AddToFiltersGraphAction } from './AddToFiltersGraphAction'; -import { getAutoQueriesForMetric } from './AutomaticMetricQueries/AutoQueryEngine'; -import { AutoQueryDef } from './AutomaticMetricQueries/types'; import { ByFrameRepeater } from './ByFrameRepeater'; import { LayoutSwitcher } from './LayoutSwitcher'; -import { MetricScene } from './MetricScene'; -import { trailDS, VAR_FILTERS, VAR_GROUP_BY, VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from './shared'; -import { getColorByIndex } from './utils'; +import { getLabelOptions } from './utils'; export interface BreakdownSceneState extends SceneObjectState { body?: SceneObject; @@ -39,13 +39,9 @@ export interface BreakdownSceneState extends SceneObjectState { loading?: boolean; } -/** - * Just a proof of concept example of a behavior - */ export class BreakdownScene extends SceneObjectBase { constructor(state: Partial) { super({ - $variables: state.$variables ?? getVariableSet(), labels: state.labels ?? [], ...state, }); @@ -84,7 +80,7 @@ export class BreakdownScene extends SceneObjectBase { } private updateBody(variable: QueryVariable) { - const options = this.getLabelOptions(variable); + const options = getLabelOptions(this, variable); const stateUpdate: Partial = { loading: variable.state.loading, @@ -101,31 +97,11 @@ export class BreakdownScene extends SceneObjectBase { this.setState(stateUpdate); } - private getLabelOptions(variable: QueryVariable) { - const labelFilters = sceneGraph.lookupVariable(VAR_FILTERS, this); - const labelOptions: Array> = []; - - if (!(labelFilters instanceof AdHocFiltersVariable)) { - return []; - } - - const filters = labelFilters.state.set.state.filters; - - for (const option of variable.getOptionsForSelect()) { - const filterExists = filters.find((f) => f.key === option.value); - if (!filterExists) { - labelOptions.push({ label: option.label, value: String(option.value) }); - } - } - - return labelOptions; - } - public onChange = (value: string) => { const variable = this.getVariable(); if (value === ALL_VARIABLE_VALUE) { - this.setState({ body: buildAllLayout(this.getLabelOptions(variable), this._query!) }); + this.setState({ body: buildAllLayout(getLabelOptions(this, variable), this._query!) }); } else if (variable.hasAllValue()) { this.setState({ body: buildNormalLayout(this._query!) }); } @@ -243,23 +219,6 @@ export function buildAllLayout(options: Array>, queryDef const GRID_TEMPLATE_COLUMNS = 'repeat(auto-fit, minmax(400px, 1fr))'; -function getVariableSet() { - return new SceneVariableSet({ - variables: [ - new QueryVariable({ - name: VAR_GROUP_BY, - label: 'Group by', - datasource: trailDS, - includeAll: true, - defaultToAll: true, - query: { query: `label_names(${VAR_METRIC_EXPR})`, refId: 'A' }, - value: '', - text: '', - }), - ], - }); -} - function buildNormalLayout(queryDef: AutoQueryDef) { return new LayoutSwitcher({ $data: new SceneQueryRunner({ diff --git a/public/app/features/trails/ByFrameRepeater.tsx b/public/app/features/trails/ActionTabs/ByFrameRepeater.tsx similarity index 100% rename from public/app/features/trails/ByFrameRepeater.tsx rename to public/app/features/trails/ActionTabs/ByFrameRepeater.tsx diff --git a/public/app/features/trails/LayoutSwitcher.tsx b/public/app/features/trails/ActionTabs/LayoutSwitcher.tsx similarity index 100% rename from public/app/features/trails/LayoutSwitcher.tsx rename to public/app/features/trails/ActionTabs/LayoutSwitcher.tsx diff --git a/public/app/features/trails/ActionTabs/LogsScene.tsx b/public/app/features/trails/ActionTabs/LogsScene.tsx new file mode 100644 index 00000000000..636f709272c --- /dev/null +++ b/public/app/features/trails/ActionTabs/LogsScene.tsx @@ -0,0 +1,22 @@ +import { PanelBuilders, SceneFlexItem, SceneQueryRunner } from '@grafana/scenes'; + +import { SelectMetricAction } from '../SelectMetricAction'; +import { LOGS_METRIC } from '../shared'; + +export function buildLogsScene() { + return new SceneFlexItem({ + $data: new SceneQueryRunner({ + queries: [ + { + refId: 'A', + datasource: { uid: 'gdev-loki' }, + expr: '{${filters}} | logfmt', + }, + ], + }), + body: PanelBuilders.logs() + .setTitle('Logs') + .setHeaderActions(new SelectMetricAction({ metric: LOGS_METRIC, title: 'Open' })) + .build(), + }); +} diff --git a/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx new file mode 100644 index 00000000000..20df46cd3eb --- /dev/null +++ b/public/app/features/trails/ActionTabs/MetricOverviewScene.tsx @@ -0,0 +1,133 @@ +import React from 'react'; + +import { + QueryVariable, + SceneComponentProps, + SceneFlexItem, + sceneGraph, + SceneObjectBase, + SceneObjectState, + VariableDependencyConfig, +} from '@grafana/scenes'; +import { Stack, Text, TextLink } from '@grafana/ui'; + +import PrometheusLanguageProvider from '../../../plugins/datasource/prometheus/language_provider'; +import { PromMetricsMetadataItem } from '../../../plugins/datasource/prometheus/types'; +import { getDatasourceSrv } from '../../plugins/datasource_srv'; +import { ALL_VARIABLE_VALUE } from '../../variables/constants'; +import { VAR_DATASOURCE_EXPR, VAR_GROUP_BY } from '../shared'; +import { getMetricSceneFor } from '../utils'; + +import { getLabelOptions } from './utils'; + +export interface MetricOverviewSceneState extends SceneObjectState { + metadata?: PromMetricsMetadataItem; + loading?: boolean; +} + +export class MetricOverviewScene extends SceneObjectBase { + protected _variableDependency = new VariableDependencyConfig(this, { + variableNames: [VAR_DATASOURCE_EXPR], + onReferencedVariableValueChanged: this.onReferencedVariableValueChanged.bind(this), + }); + + constructor(state: Partial) { + super({ + ...state, + }); + + this.addActivationHandler(this._onActivate.bind(this)); + } + + private getVariable(): QueryVariable { + const variable = sceneGraph.lookupVariable(VAR_GROUP_BY, this)!; + if (!(variable instanceof QueryVariable)) { + throw new Error('Group by variable not found'); + } + + return variable; + } + + private _onActivate() { + this.updateMetadata(); + } + + private onReferencedVariableValueChanged() { + this.updateMetadata(); + } + + private async updateMetadata() { + const ds = await getDatasourceSrv().get(VAR_DATASOURCE_EXPR, { __sceneObject: { value: this } }); + + const languageProvider: PrometheusLanguageProvider = ds.languageProvider; + + if (!languageProvider) { + return; + } + + const metricScene = getMetricSceneFor(this); + const metric = metricScene.state.metric; + + if (languageProvider.metricsMetadata) { + this.setState({ metadata: languageProvider.metricsMetadata[metric] }); + return; + } + + await languageProvider.start(); + + this.setState({ metadata: languageProvider.metricsMetadata?.[metric] }); + } + + public static Component = ({ model }: SceneComponentProps) => { + const { metadata } = model.useState(); + const variable = model.getVariable(); + const { loading } = variable.useState(); + const labelOptions = getLabelOptions(model, variable).filter((l) => l.value !== ALL_VARIABLE_VALUE); + + return ( + + {loading ? ( +
Loading...
+ ) : ( + <> + + Description + {metadata?.help ?
{metadata?.help}
: No description available} +
+ + Type + {metadata?.type ?
{metadata?.type}
: Unknown} +
+ + Unit + {metadata?.unit ?
{metadata?.unit}
: Unknown} +
+ + Labels + {labelOptions.map((l) => ( + + {l.label!} + + ))} + + + )} +
+ ); + }; +} + +export function buildMetricOverviewScene() { + return new SceneFlexItem({ + body: new MetricOverviewScene({}), + }); +} diff --git a/public/app/features/trails/ActionTabs/RelatedMetricsScene.tsx b/public/app/features/trails/ActionTabs/RelatedMetricsScene.tsx new file mode 100644 index 00000000000..6ae4b915c9f --- /dev/null +++ b/public/app/features/trails/ActionTabs/RelatedMetricsScene.tsx @@ -0,0 +1,9 @@ +import { SceneFlexItem } from '@grafana/scenes'; + +import { MetricSelectScene } from '../MetricSelectScene'; + +export function buildRelatedMetricsScene() { + return new SceneFlexItem({ + body: new MetricSelectScene({}), + }); +} diff --git a/public/app/features/trails/ActionTabs/utils.ts b/public/app/features/trails/ActionTabs/utils.ts new file mode 100644 index 00000000000..8934594f243 --- /dev/null +++ b/public/app/features/trails/ActionTabs/utils.ts @@ -0,0 +1,24 @@ +import { SelectableValue } from '@grafana/data'; +import { AdHocFiltersVariable, QueryVariable, sceneGraph, SceneObject } from '@grafana/scenes'; + +import { VAR_FILTERS } from '../shared'; + +export function getLabelOptions(scenObject: SceneObject, variable: QueryVariable) { + const labelFilters = sceneGraph.lookupVariable(VAR_FILTERS, scenObject); + const labelOptions: Array> = []; + + if (!(labelFilters instanceof AdHocFiltersVariable)) { + return []; + } + + const filters = labelFilters.state.set.state.filters; + + for (const option of variable.getOptionsForSelect()) { + const filterExists = filters.find((f) => f.key === option.value); + if (!filterExists) { + labelOptions.push({ label: option.label, value: String(option.value) }); + } + } + + return labelOptions; +} diff --git a/public/app/features/trails/DataTrailsHome.tsx b/public/app/features/trails/DataTrailsHome.tsx index 1d16c6ad43b..e186e2ffdf5 100644 --- a/public/app/features/trails/DataTrailsHome.tsx +++ b/public/app/features/trails/DataTrailsHome.tsx @@ -10,7 +10,7 @@ import { DataTrail } from './DataTrail'; import { DataTrailCard } from './DataTrailCard'; import { DataTrailsApp } from './DataTrailsApp'; import { getTrailStore } from './TrailStore/TrailStore'; -import { newMetricsTrail } from './utils'; +import { getDatasourceForNewTrail, newMetricsTrail } from './utils'; export interface DataTrailsHomeState extends SceneObjectState {} @@ -21,7 +21,7 @@ export class DataTrailsHome extends SceneObjectBase { public onNewMetricsTrail = () => { const app = getAppFor(this); - const trail = newMetricsTrail(); + const trail = newMetricsTrail(getDatasourceForNewTrail()); getTrailStore().setRecentTrail(trail); app.goToUrlForTrail(trail); diff --git a/public/app/features/trails/MetricScene.tsx b/public/app/features/trails/MetricScene.tsx index 5fa886265c2..91d57b1bafe 100644 --- a/public/app/features/trails/MetricScene.tsx +++ b/public/app/features/trails/MetricScene.tsx @@ -1,33 +1,38 @@ +import { css } from '@emotion/css'; import React, { useState } from 'react'; -import { DashboardCursorSync } from '@grafana/data'; +import { DashboardCursorSync, GrafanaTheme2 } from '@grafana/data'; import { SceneObjectState, SceneObjectBase, SceneComponentProps, SceneFlexLayout, SceneFlexItem, - SceneQueryRunner, SceneObjectUrlSyncConfig, SceneObjectUrlValues, - PanelBuilders, sceneGraph, + SceneVariableSet, + QueryVariable, behaviors, } from '@grafana/scenes'; -import { ToolbarButton, Box, Stack, Icon } from '@grafana/ui'; +import { ToolbarButton, Box, Stack, Icon, TabsBar, Tab, useStyles2 } from '@grafana/ui'; +import { buildBreakdownActionScene } from './ActionTabs/BreakdownScene'; +import { buildLogsScene } from './ActionTabs/LogsScene'; +import { buildMetricOverviewScene } from './ActionTabs/MetricOverviewScene'; +import { buildRelatedMetricsScene } from './ActionTabs/RelatedMetricsScene'; import { getAutoQueriesForMetric } from './AutomaticMetricQueries/AutoQueryEngine'; import { AutoVizPanel } from './AutomaticMetricQueries/AutoVizPanel'; -import { buildBreakdownActionScene } from './BreakdownScene'; -import { MetricSelectScene } from './MetricSelectScene'; -import { SelectMetricAction } from './SelectMetricAction'; import { getTrailStore } from './TrailStore/TrailStore'; import { ActionViewDefinition, + ActionViewType, getVariablesWithMetricConstant, - LOGS_METRIC, MakeOptional, OpenEmbeddedTrailEvent, + trailDS, + VAR_GROUP_BY, + VAR_METRIC_EXPR, } from './shared'; import { getTrailFor } from './utils'; @@ -42,7 +47,7 @@ export class MetricScene extends SceneObjectBase { public constructor(state: MakeOptional) { super({ - $variables: state.$variables ?? getVariablesWithMetricConstant(state.metric), + $variables: state.$variables ?? getVariableSet(state.metric), body: state.body ?? buildGraphScene(state.metric), ...state, }); @@ -57,7 +62,7 @@ export class MetricScene extends SceneObjectBase { if (this.state.actionView !== values.actionView) { const actionViewDef = actionViewsDefinitions.find((v) => v.value === values.actionView); if (actionViewDef) { - this.setActionView(actionViewDef); + this.setActionView(actionViewDef.value); } } } else if (values.actionView === null) { @@ -65,8 +70,9 @@ export class MetricScene extends SceneObjectBase { } } - public setActionView(actionViewDef?: ActionViewDefinition) { + public setActionView(actionView?: ActionViewType) { const { body } = this.state; + const actionViewDef = actionViewsDefinitions.find((v) => v.value === actionView); if (actionViewDef && actionViewDef.value !== this.state.actionView) { // reduce max height for main panel to reduce height flicker @@ -88,6 +94,7 @@ export class MetricScene extends SceneObjectBase { } const actionViewsDefinitions: ActionViewDefinition[] = [ + { displayName: 'Overview', value: 'overview', getScene: buildMetricOverviewScene }, { displayName: 'Breakdown', value: 'breakdown', getScene: buildBreakdownActionScene }, { displayName: 'Logs', value: 'logs', getScene: buildLogsScene }, { displayName: 'Related metrics', value: 'related', getScene: buildRelatedMetricsScene }, @@ -96,16 +103,13 @@ const actionViewsDefinitions: ActionViewDefinition[] = [ export interface MetricActionBarState extends SceneObjectState {} export class MetricActionBar extends SceneObjectBase { - public getButtonVariant(actionViewName: string, currentView: string | undefined) { - return currentView === actionViewName ? 'active' : 'canvas'; - } - public onOpenTrail = () => { this.publishEvent(new OpenEmbeddedTrailEvent(), true); }; public static Component = ({ model }: SceneComponentProps) => { const metricScene = sceneGraph.getAncestor(model, MetricScene); + const styles = useStyles2(getStyles); const trail = getTrailFor(model); const [isBookmarked, setBookmarked] = useState(false); const { actionView } = metricScene.useState(); @@ -115,49 +119,92 @@ export class MetricActionBar extends SceneObjectBase { setBookmarked(!isBookmarked); }; + if (!actionView) { + metricScene.setActionView('overview'); + } + return ( - - {actionViewsDefinitions.map((viewDef) => ( +
+ + + Explore + + Add to dashboard + metricScene.setActionView(viewDef)} - > - {viewDef.displayName} - - ))} - Add to dashboard - - - ) : ( - - ) - } - tooltip={'Bookmark'} - onClick={onBookmarkTrail} - /> - - {trail.state.embedded && ( - - Open - - )} - + variant={'canvas'} + icon={ + isBookmarked ? ( + + ) : ( + + ) + } + tooltip={'Bookmark'} + onClick={onBookmarkTrail} + /> + {trail.state.embedded && ( + + Open + + )} + +
+ + + {actionViewsDefinitions.map((tab, index) => { + return ( + metricScene.setActionView(tab.value)} + /> + ); + })} +
); }; } +function getStyles(theme: GrafanaTheme2) { + return { + actions: css({ + [theme.breakpoints.up(theme.breakpoints.values.md)]: { + position: 'absolute', + right: 0, + zIndex: 2, + }, + }), + }; +} + +function getVariableSet(metric: string) { + return new SceneVariableSet({ + variables: [ + ...getVariablesWithMetricConstant(metric), + new QueryVariable({ + name: VAR_GROUP_BY, + label: 'Group by', + datasource: trailDS, + includeAll: true, + defaultToAll: true, + query: { query: `label_names(${VAR_METRIC_EXPR})`, refId: 'A' }, + value: '', + text: '', + }), + ], + }); +} + const MAIN_PANEL_MIN_HEIGHT = 280; const MAIN_PANEL_MAX_HEIGHT = '40%'; function buildGraphScene(metric: string) { const autoQuery = getAutoQueriesForMetric(metric); + const bodyAutoVizPanel = new AutoVizPanel({ autoQuery }); return new SceneFlexLayout({ direction: 'column', @@ -166,7 +213,7 @@ function buildGraphScene(metric: string) { new SceneFlexItem({ minHeight: MAIN_PANEL_MIN_HEIGHT, maxHeight: MAIN_PANEL_MAX_HEIGHT, - body: new AutoVizPanel({ autoQuery }), + body: bodyAutoVizPanel, }), new SceneFlexItem({ ySizing: 'content', @@ -175,27 +222,3 @@ function buildGraphScene(metric: string) { ], }); } - -function buildLogsScene() { - return new SceneFlexItem({ - $data: new SceneQueryRunner({ - queries: [ - { - refId: 'A', - datasource: { uid: 'gdev-loki' }, - expr: '{${filters}} | logfmt', - }, - ], - }), - body: PanelBuilders.logs() - .setTitle('Logs') - .setHeaderActions(new SelectMetricAction({ metric: LOGS_METRIC, title: 'Open' })) - .build(), - }); -} - -function buildRelatedMetricsScene() { - return new SceneFlexItem({ - body: new MetricSelectScene({}), - }); -} diff --git a/public/app/features/trails/MetricSelectScene.tsx b/public/app/features/trails/MetricSelectScene.tsx index 7be4a119b09..d0de55a95e9 100644 --- a/public/app/features/trails/MetricSelectScene.tsx +++ b/public/app/features/trails/MetricSelectScene.tsx @@ -181,7 +181,9 @@ export class MetricSelectScene extends SceneObjectBase { children.push(panel); } else { const panel = new SceneCSSGridItem({ - $variables: getVariablesWithMetricConstant(metric.name), + $variables: new SceneVariableSet({ + variables: getVariablesWithMetricConstant(metric.name), + }), body: getCardPanelFor(metric.name), }); metric.itemRef = panel.getRef(); @@ -263,7 +265,9 @@ function getPreviewPanelFor(metric: string, index: number) { .build(); return new SceneCSSGridItem({ - $variables: getVariablesWithMetricConstant(metric), + $variables: new SceneVariableSet({ + variables: getVariablesWithMetricConstant(metric), + }), $behaviors: [hideEmptyPreviews(metric)], $data: new SceneQueryRunner({ datasource: trailDS, diff --git a/public/app/features/trails/SelectMetricTrailView.tsx b/public/app/features/trails/SelectMetricTrailView.tsx deleted file mode 100644 index bea975cec52..00000000000 --- a/public/app/features/trails/SelectMetricTrailView.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; - -import { - SceneObjectState, - SceneObjectBase, - VariableDependencyConfig, - sceneGraph, - SceneComponentProps, - SceneVariableSet, - SceneVariable, - QueryVariable, - VariableValueOption, -} from '@grafana/scenes'; -import { VariableHide } from '@grafana/schema'; -import { Input, Card, Stack } from '@grafana/ui'; - -import { trailDS } from './shared'; - -export interface SelectMetricTrailViewState extends SceneObjectState { - metricNames: VariableValueOption[]; -} - -export class SelectMetricTrailView extends SceneObjectBase { - public constructor(state: Partial) { - super({ - $variables: new SceneVariableSet({ - variables: [ - new QueryVariable({ - name: 'metricNames', - datasource: trailDS, - hide: VariableHide.hideVariable, - includeAll: true, - defaultToAll: true, - skipUrlSync: true, - query: { query: 'label_values({$filters},__name__)', refId: 'A' }, - }), - ], - }), - metricNames: [], - ...state, - }); - } - - protected _variableDependency = new VariableDependencyConfig(this, { - variableNames: ['filters', 'metricNames'], - onVariableUpdatesCompleted: this._onVariableChanged.bind(this), - }); - - private _onVariableChanged(changedVariables: Set, dependencyChanged: boolean): void { - for (const variable of changedVariables) { - if (variable.state.name === 'filters') { - const variable = sceneGraph.lookupVariable('filters', this)!; - // Temp hack - (this.state.$variables as any)._handleVariableValueChanged(variable); - } - - if (variable.state.name === 'metricNames' && variable instanceof QueryVariable) { - this.setState({ metricNames: variable.state.options }); - } - } - } - - static Component = ({ model }: SceneComponentProps) => { - const { metricNames } = model.useState(); - - return ( - - - -
-
- {metricNames.map((option, index) => ( - - {String(option.value)} - - ))} -
- ); - }; -} diff --git a/public/app/features/trails/shared.ts b/public/app/features/trails/shared.ts index a33cc4254dc..2cb3fb479a8 100644 --- a/public/app/features/trails/shared.ts +++ b/public/app/features/trails/shared.ts @@ -1,10 +1,11 @@ import { BusEventBase, BusEventWithPayload } from '@grafana/data'; -import { ConstantVariable, SceneObject, SceneVariableSet } from '@grafana/scenes'; +import { ConstantVariable, SceneObject } from '@grafana/scenes'; import { VariableHide } from '@grafana/schema'; +export type ActionViewType = 'overview' | 'breakdown' | 'logs' | 'related'; export interface ActionViewDefinition { displayName: string; - value: string; + value: ActionViewType; getScene: () => SceneObject; } @@ -32,15 +33,13 @@ export const BOOKMARKED_TRAILS_KEY = 'grafana.trails.bookmarks'; export type MakeOptional = Pick, K> & Omit; export function getVariablesWithMetricConstant(metric: string) { - return new SceneVariableSet({ - variables: [ - new ConstantVariable({ - name: VAR_METRIC, - value: metric, - hide: VariableHide.hideVariable, - }), - ], - }); + return [ + new ConstantVariable({ + name: VAR_METRIC, + value: metric, + hide: VariableHide.hideVariable, + }), + ]; } export class MetricSelectedEvent extends BusEventWithPayload { diff --git a/public/app/features/trails/utils.ts b/public/app/features/trails/utils.ts index 463b6752dec..4ce16518f24 100644 --- a/public/app/features/trails/utils.ts +++ b/public/app/features/trails/utils.ts @@ -2,10 +2,13 @@ import { urlUtil } from '@grafana/data'; import { config } from '@grafana/runtime'; import { getUrlSyncManager, sceneGraph, SceneObject, SceneObjectUrlValues, SceneTimeRange } from '@grafana/scenes'; +import { getDatasourceSrv } from '../plugins/datasource_srv'; + import { DataTrail } from './DataTrail'; import { DataTrailSettings } from './DataTrailSettings'; import { MetricScene } from './MetricScene'; -import { TRAILS_ROUTE } from './shared'; +import { getTrailStore } from './TrailStore/TrailStore'; +import { TRAILS_ROUTE, VAR_DATASOURCE_EXPR } from './shared'; export function getTrailFor(model: SceneObject): DataTrail { return sceneGraph.getAncestor(model, DataTrail); @@ -15,9 +18,9 @@ export function getTrailSettings(model: SceneObject): DataTrailSettings { return sceneGraph.getAncestor(model, DataTrail).state.settings; } -export function newMetricsTrail(): DataTrail { +export function newMetricsTrail(initialDS?: string): DataTrail { return new DataTrail({ - //initialDS: 'gdev-prometheus', + initialDS, $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), //initialFilters: [{ key: 'job', operator: '=', value: 'grafana' }], embedded: false, @@ -47,6 +50,21 @@ export function getMetricSceneFor(model: SceneObject): MetricScene { throw new Error('Unable to find trail'); } +export function getDatasourceForNewTrail(): string | undefined { + const prevTrail = getTrailStore().recent[0]; + if (prevTrail) { + const prevDataSource = sceneGraph.interpolate(prevTrail.resolve(), VAR_DATASOURCE_EXPR); + if (typeof prevDataSource === 'string' && prevDataSource.length > 0) { + return prevDataSource; + } + } + const promDatasources = getDatasourceSrv().getList({ type: 'prometheus' }); + if (promDatasources.length > 0) { + return promDatasources.find((mds) => mds.uid === config.defaultDatasource)?.uid ?? promDatasources[0].uid; + } + return undefined; +} + export function getColorByIndex(index: number) { const visTheme = config.theme2.visualization; return visTheme.getColorByName(visTheme.palette[index % 8]);