import { UrlQueryMap, urlUtil } from '@grafana/data'; import { config, locationSearchToObject } from '@grafana/runtime'; import { MultiValueVariable, SceneDataTransformer, sceneGraph, SceneObject, SceneQueryRunner, VizPanel, } from '@grafana/scenes'; import { DashboardScene } from '../scene/DashboardScene'; export function getVizPanelKeyForPanelId(panelId: number) { return `panel-${panelId}`; } export function getPanelIdForVizPanel(panel: SceneObject): number { return parseInt(panel.state.key!.replace('panel-', ''), 10); } /** * This will also try lookup based on panelId */ export function findVizPanelByKey(scene: SceneObject, key: string | undefined): VizPanel | null { if (!key) { return null; } const panel = findVizPanelInternal(scene, key); if (panel) { return panel; } // Also try to find by panel id const id = parseInt(key, 10); if (isNaN(id)) { return null; } return findVizPanelInternal(scene, getVizPanelKeyForPanelId(id)); } function findVizPanelInternal(scene: SceneObject, key: string | undefined): VizPanel | null { if (!key) { return null; } const panel = sceneGraph.findObject(scene, (obj) => obj.state.key === key); if (panel) { if (panel instanceof VizPanel) { return panel; } else { throw new Error(`Found panel with key ${key} but it was not a VizPanel`); } } return null; } /** * Force re-render children. This is useful in some edge case scenarios when * children deep down the scene graph needs to be re-rendered when some parent state change. * * Example could be isEditing bool flag or a layout IsDraggable state flag. * * @param model The model whose children should be re-rendered. It does not force render this model, only the children. * @param recursive if it should keep force rendering down to leaf nodess */ export function forceRenderChildren(model: SceneObject, recursive?: boolean) { model.forEachChild((child) => { if (!child.isActive) { return; } child.forceRender(); forceRenderChildren(child, recursive); }); } export interface DashboardUrlOptions { uid?: string; subPath?: string; updateQuery?: UrlQueryMap; /** Set to location.search to preserve current params */ currentQueryParams: string; /** * Returns solo panel route instead */ soloRoute?: boolean; /** return render url */ render?: boolean; /** Return an absolute URL */ absolute?: boolean; // Add tz to query params timeZone?: string; } export function getDashboardUrl(options: DashboardUrlOptions) { let path = `/scenes/dashboard/${options.uid}${options.subPath ?? ''}`; if (options.soloRoute) { path = `/d-solo/${options.uid}${options.subPath ?? ''}`; } if (options.render) { path = '/render' + path; options.updateQuery = { ...options.updateQuery, width: 1000, height: 500, tz: options.timeZone, }; } const params = options.currentQueryParams ? locationSearchToObject(options.currentQueryParams) : {}; if (options.updateQuery) { for (const key of Object.keys(options.updateQuery)) { // removing params with null | undefined if (options.updateQuery[key] === null || options.updateQuery[key] === undefined) { delete params[key]; } else { params[key] = options.updateQuery[key]; } } } const relativeUrl = urlUtil.renderUrl(path, params); if (options.absolute) { return config.appUrl + relativeUrl.slice(1); } return relativeUrl; } export function getMultiVariableValues(variable: MultiValueVariable) { const { value, text, options } = variable.state; if (variable.hasAllValue()) { return { values: options.map((o) => o.value), texts: options.map((o) => o.label), }; } return { values: Array.isArray(value) ? value : [value], texts: Array.isArray(text) ? text : [text], }; } export function getQueryRunnerFor(sceneObject: SceneObject | undefined): SceneQueryRunner | undefined { if (!sceneObject) { return undefined; } if (sceneObject.state.$data instanceof SceneQueryRunner) { return sceneObject.state.$data; } if (sceneObject.state.$data instanceof SceneDataTransformer) { return getQueryRunnerFor(sceneObject.state.$data); } return undefined; } export function getDashboardSceneFor(sceneObject: SceneObject): DashboardScene { const root = sceneObject.getRoot(); if (root instanceof DashboardScene) { return root; } throw new Error('SceneObject root is not a DashboardScene'); }