mirror of https://github.com/grafana/grafana
DashboardScene: Adds solo page that uses dasboarde scene to render single panel (#77940)
* DashboardScene: Adds solo page that uses dasboarde scene to render single panel * Update * Panel and row repeats working * Update * added e2e tests * Refactor * Fixes * Fix e2e * fix * fix * fixpull/82292/head
parent
02c0f5929c
commit
fe6d1460b0
|
@ -0,0 +1,63 @@ |
||||
// Libraries
|
||||
import React, { useEffect } from 'react'; |
||||
|
||||
import { Alert, Spinner } from '@grafana/ui'; |
||||
import PageLoader from 'app/core/components/PageLoader/PageLoader'; |
||||
import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound'; |
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; |
||||
import { DashboardPageRouteParams } from 'app/features/dashboard/containers/types'; |
||||
import { DashboardRoutes } from 'app/types'; |
||||
|
||||
import { getDashboardScenePageStateManager } from '../pages/DashboardScenePageStateManager'; |
||||
import { DashboardScene } from '../scene/DashboardScene'; |
||||
|
||||
import { useSoloPanel } from './useSoloPanel'; |
||||
|
||||
export interface Props extends GrafanaRouteComponentProps<DashboardPageRouteParams, { panelId: string }> {} |
||||
|
||||
/** |
||||
* Used for iframe embedding and image rendering of single panels |
||||
*/ |
||||
export function SoloPanelPage({ match, queryParams }: Props) { |
||||
const stateManager = getDashboardScenePageStateManager(); |
||||
const { dashboard } = stateManager.useState(); |
||||
|
||||
useEffect(() => { |
||||
stateManager.loadDashboard({ uid: match.params.uid!, route: DashboardRoutes.Embedded }); |
||||
return () => stateManager.clearState(); |
||||
}, [stateManager, match, queryParams]); |
||||
|
||||
if (!queryParams.panelId) { |
||||
return <EntityNotFound entity="Panel" />; |
||||
} |
||||
|
||||
if (!dashboard) { |
||||
return <PageLoader />; |
||||
} |
||||
|
||||
return <SoloPanelRenderer dashboard={dashboard} panelId={queryParams.panelId} />; |
||||
} |
||||
|
||||
export default SoloPanelPage; |
||||
|
||||
export function SoloPanelRenderer({ dashboard, panelId }: { dashboard: DashboardScene; panelId: string }) { |
||||
const [panel, error] = useSoloPanel(dashboard, panelId); |
||||
|
||||
if (error) { |
||||
return <Alert title={error} />; |
||||
} |
||||
|
||||
if (!panel) { |
||||
return ( |
||||
<span> |
||||
Loading <Spinner /> |
||||
</span> |
||||
); |
||||
} |
||||
|
||||
return ( |
||||
<div className="panel-solo"> |
||||
<panel.Component model={panel} /> |
||||
</div> |
||||
); |
||||
} |
||||
@ -0,0 +1,84 @@ |
||||
import { useState, useEffect } from 'react'; |
||||
|
||||
import { VizPanel, SceneObject, SceneGridRow, getUrlSyncManager } from '@grafana/scenes'; |
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene'; |
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem'; |
||||
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; |
||||
import { DashboardRepeatsProcessedEvent } from '../scene/types'; |
||||
import { findVizPanelByKey, isPanelClone } from '../utils/utils'; |
||||
|
||||
export function useSoloPanel(dashboard: DashboardScene, panelId: string): [VizPanel | undefined, string | undefined] { |
||||
const [panel, setPanel] = useState<VizPanel>(); |
||||
const [error, setError] = useState<string | undefined>(); |
||||
|
||||
useEffect(() => { |
||||
getUrlSyncManager().initSync(dashboard); |
||||
|
||||
const cleanUp = dashboard.activate(); |
||||
|
||||
const panel = findVizPanelByKey(dashboard, panelId); |
||||
if (panel) { |
||||
activateParents(panel); |
||||
setPanel(panel); |
||||
} else if (isPanelClone(panelId)) { |
||||
findRepeatClone(dashboard, panelId).then((panel) => { |
||||
if (panel) { |
||||
setPanel(panel); |
||||
} else { |
||||
setError('Panel not found'); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
return cleanUp; |
||||
}, [dashboard, panelId]); |
||||
|
||||
return [panel, error]; |
||||
} |
||||
|
||||
function activateParents(panel: VizPanel) { |
||||
let parent = panel.parent; |
||||
|
||||
while (parent && !parent.isActive) { |
||||
parent.activate(); |
||||
parent = parent.parent; |
||||
} |
||||
} |
||||
|
||||
function findRepeatClone(dashboard: DashboardScene, panelId: string): Promise<VizPanel | undefined> { |
||||
return new Promise((resolve) => { |
||||
dashboard.subscribeToEvent(DashboardRepeatsProcessedEvent, () => { |
||||
const panel = findVizPanelByKey(dashboard, panelId); |
||||
if (panel) { |
||||
resolve(panel); |
||||
} else { |
||||
// If rows are repeated they could add new panel repeaters that needs to be activated
|
||||
activateAllRepeaters(dashboard.state.body); |
||||
} |
||||
}); |
||||
|
||||
activateAllRepeaters(dashboard.state.body); |
||||
}); |
||||
} |
||||
|
||||
function activateAllRepeaters(layout: SceneObject) { |
||||
layout.forEachChild((child) => { |
||||
if (child instanceof PanelRepeaterGridItem && !child.isActive) { |
||||
child.activate(); |
||||
return; |
||||
} |
||||
|
||||
if (child instanceof SceneGridRow && child.state.$behaviors) { |
||||
for (const behavior of child.state.$behaviors) { |
||||
if (behavior instanceof RowRepeaterBehavior && !child.isActive) { |
||||
child.activate(); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// Activate any panel PanelRepeaterGridItem inside the row
|
||||
activateAllRepeaters(child); |
||||
} |
||||
}); |
||||
} |
||||
Loading…
Reference in new issue