mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard-Scene: View panel as table in edit mode (#83077)
* WIP: working functionality * betterer * Fully working: Alerts show up, toggling table view doesn't update viz type in options pane * betterer * improve * betterer * Refactoring a bit * wrong step * move data provider to vizPanel * Update * update * More refactorings * Fix InspectJsonTab tests (except 1); remove obsolete PanelControls * Fixed test * Update * minor fix --------- Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
parent
a564c8c439
commit
28fa2849df
@ -1,12 +1,21 @@
|
||||
import { FieldType, getDefaultTimeRange, LoadingState, standardTransformersRegistry, toDataFrame } from '@grafana/data';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import {
|
||||
FieldType,
|
||||
getDefaultTimeRange,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
standardTransformersRegistry,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||
import { setPluginImportUtils } from '@grafana/runtime';
|
||||
import { setPluginImportUtils, setRunRequest } from '@grafana/runtime';
|
||||
import {
|
||||
SceneCanvasText,
|
||||
SceneDataNode,
|
||||
SceneDataTransformer,
|
||||
SceneGridItem,
|
||||
SceneGridLayout,
|
||||
SceneQueryRunner,
|
||||
VizPanel,
|
||||
} from '@grafana/scenes';
|
||||
import * as libpanels from 'app/features/library-panels/state/api';
|
||||
@ -34,8 +43,45 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getPluginLinkExtensions: jest.fn(() => ({
|
||||
extensions: [],
|
||||
})),
|
||||
getDataSourceSrv: () => {
|
||||
return {
|
||||
get: jest.fn().mockResolvedValue({
|
||||
getRef: () => ({ uid: 'ds1' }),
|
||||
}),
|
||||
getInstanceSettings: jest.fn().mockResolvedValue({ uid: 'ds1' }),
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
const runRequestMock = jest.fn().mockReturnValue(
|
||||
of<PanelData>({
|
||||
state: LoadingState.Done,
|
||||
timeRange: getDefaultTimeRange(),
|
||||
series: [
|
||||
toDataFrame({
|
||||
fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3] }],
|
||||
}),
|
||||
],
|
||||
request: {
|
||||
app: 'dashboard',
|
||||
requestId: 'request-id',
|
||||
dashboardUID: 'asd',
|
||||
interval: '1s',
|
||||
panelId: 1,
|
||||
range: getDefaultTimeRange(),
|
||||
targets: [],
|
||||
timezone: 'utc',
|
||||
intervalMs: 1000,
|
||||
startTime: 1,
|
||||
scopedVars: {
|
||||
__sceneObject: { value: new SceneCanvasText({ text: 'asd' }) },
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
setRunRequest(runRequestMock);
|
||||
|
||||
describe('InspectJsonTab', () => {
|
||||
it('Can show panel json', async () => {
|
||||
const { tab } = await buildTestScene();
|
||||
@ -121,31 +167,9 @@ function buildTestPanel() {
|
||||
},
|
||||
},
|
||||
],
|
||||
$data: new SceneDataNode({
|
||||
data: {
|
||||
state: LoadingState.Done,
|
||||
series: [
|
||||
toDataFrame({
|
||||
fields: [{ name: 'value', type: FieldType.number, values: [1, 2, 3] }],
|
||||
}),
|
||||
],
|
||||
timeRange: getDefaultTimeRange(),
|
||||
request: {
|
||||
app: 'dashboard',
|
||||
requestId: 'request-id',
|
||||
dashboardUID: 'asd',
|
||||
interval: '1s',
|
||||
panelId: 1,
|
||||
range: getDefaultTimeRange(),
|
||||
targets: [],
|
||||
timezone: 'utc',
|
||||
intervalMs: 1000,
|
||||
startTime: 1,
|
||||
scopedVars: {
|
||||
__sceneObject: { value: new SceneCanvasText({ text: 'asd' }) },
|
||||
},
|
||||
},
|
||||
},
|
||||
$data: new SceneQueryRunner({
|
||||
datasource: { uid: 'abcdef' },
|
||||
queries: [{ refId: 'A' }],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
@ -26,6 +26,7 @@ import { InspectTab } from 'app/features/inspector/types';
|
||||
import { getPrettyJSON } from 'app/features/inspector/utils/utils';
|
||||
import { reportPanelInspectInteraction } from 'app/features/search/page/reporting';
|
||||
|
||||
import { VizPanelManager } from '../panel-edit/VizPanelManager';
|
||||
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||
import { buildGridItemForPanel } from '../serialization/transformSaveModelToScene';
|
||||
@ -60,7 +61,7 @@ export class InspectJsonTab extends SceneObjectBase<InspectJsonTabState> {
|
||||
|
||||
public getOptions(): Array<SelectableValue<ShowContent>> {
|
||||
const panel = this.state.panelRef.resolve();
|
||||
const dataProvider = panel.state.$data;
|
||||
const dataProvider = panel.state.$data ?? panel.parent?.state.$data;
|
||||
|
||||
const options: Array<SelectableValue<ShowContent>> = [
|
||||
{
|
||||
@ -201,10 +202,14 @@ function getJsonText(show: ShowContent, panel: VizPanel): string {
|
||||
case 'panel-json': {
|
||||
reportPanelInspectInteraction(InspectTab.JSON, 'panelData');
|
||||
|
||||
if (panel.parent instanceof SceneGridItem || panel.parent instanceof PanelRepeaterGridItem) {
|
||||
objToStringify = gridItemToPanel(panel.parent);
|
||||
} else if (panel.parent instanceof LibraryVizPanel) {
|
||||
const parent = panel.parent!;
|
||||
|
||||
if (parent instanceof SceneGridItem || parent instanceof PanelRepeaterGridItem) {
|
||||
objToStringify = gridItemToPanel(parent);
|
||||
} else if (parent instanceof LibraryVizPanel) {
|
||||
objToStringify = libraryPanelChildToLegacyRepresentation(panel);
|
||||
} else if (parent instanceof VizPanelManager) {
|
||||
objToStringify = parent.getPanelSaveModel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -246,18 +251,22 @@ function libraryPanelChildToLegacyRepresentation(panel: VizPanel<{}, {}>) {
|
||||
if (!(panel.parent instanceof LibraryVizPanel)) {
|
||||
throw 'Panel not child of LibraryVizPanel';
|
||||
}
|
||||
|
||||
if (!(panel.parent.parent instanceof SceneGridItem)) {
|
||||
throw 'LibraryPanel not child of SceneGridItem';
|
||||
}
|
||||
|
||||
const gridItem = panel.parent.parent;
|
||||
const libraryPanelObj = gridItemToPanel(gridItem);
|
||||
const panelObj = vizPanelToPanel(panel);
|
||||
|
||||
panelObj.gridPos = {
|
||||
x: gridItem.state.x || 0,
|
||||
y: gridItem.state.y || 0,
|
||||
h: gridItem.state.height || 0,
|
||||
w: gridItem.state.width || 0,
|
||||
};
|
||||
|
||||
return { ...libraryPanelObj, ...panelObj };
|
||||
}
|
||||
|
||||
|
@ -349,8 +349,8 @@ async function clickNewButton() {
|
||||
|
||||
function createModel(dashboard: DashboardModel) {
|
||||
const scene = createDashboardSceneFromDashboardModel(dashboard);
|
||||
const vizPanel = findVizPanelByKey(scene, getVizPanelKeyForPanelId(34));
|
||||
const model = new PanelDataAlertingTab(new VizPanelManager(vizPanel!.clone()));
|
||||
const vizPanel = findVizPanelByKey(scene, getVizPanelKeyForPanelId(34))!;
|
||||
const model = new PanelDataAlertingTab(VizPanelManager.createFor(vizPanel));
|
||||
jest.spyOn(utils, 'getDashboardSceneFor').mockReturnValue(scene);
|
||||
return model;
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ const setupVizPanelManger = (panelId: string) => {
|
||||
const scene = transformSaveModelToScene({ dashboard: testDashboard as unknown as DashboardDataDTO, meta: {} });
|
||||
const panel = findVizPanelByKey(scene, panelId)!;
|
||||
|
||||
const vizPanelManager = new VizPanelManager(panel.clone());
|
||||
const vizPanelManager = VizPanelManager.createFor(panel);
|
||||
|
||||
// The following happens on DahsboardScene activation. For the needs of this test this activation aint needed hence we hand-call it
|
||||
// @ts-expect-error
|
||||
|
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { InlineSwitch } from '@grafana/ui';
|
||||
|
||||
import { PanelEditor } from './PanelEditor';
|
||||
|
||||
export interface Props {
|
||||
panelEditor: PanelEditor;
|
||||
}
|
||||
|
||||
export function PanelEditControls({ panelEditor }: Props) {
|
||||
const vizManager = panelEditor.state.vizManager;
|
||||
const { panel, tableView } = vizManager.useState();
|
||||
const skipDataQuery = config.panels[panel.state.pluginId].skipDataQuery;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!skipDataQuery && (
|
||||
<InlineSwitch
|
||||
label="Table view"
|
||||
showLabel={true}
|
||||
id="table-view"
|
||||
value={tableView ? true : false}
|
||||
onClick={() => vizManager.toggleTableView()}
|
||||
aria-label="toggle-table-view"
|
||||
data-testid={selectors.components.PanelEditor.toggleTableView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,14 +2,9 @@ import * as H from 'history';
|
||||
|
||||
import { NavIndex } from '@grafana/data';
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { SceneGridItem, SceneObject, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
|
||||
import { SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
|
||||
|
||||
import {
|
||||
findVizPanelByKey,
|
||||
getDashboardSceneFor,
|
||||
getPanelIdForVizPanel,
|
||||
getVizPanelKeyForPanelId,
|
||||
} from '../utils/utils';
|
||||
import { getDashboardSceneFor, getPanelIdForVizPanel } from '../utils/utils';
|
||||
|
||||
import { PanelDataPane } from './PanelDataPane/PanelDataPane';
|
||||
import { PanelEditorRenderer } from './PanelEditorRenderer';
|
||||
@ -17,7 +12,6 @@ import { PanelOptionsPane } from './PanelOptionsPane';
|
||||
import { VizPanelManager } from './VizPanelManager';
|
||||
|
||||
export interface PanelEditorState extends SceneObjectState {
|
||||
controls?: SceneObject[];
|
||||
isDirty?: boolean;
|
||||
panelId: number;
|
||||
optionsPane: PanelOptionsPane;
|
||||
@ -32,7 +26,6 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
||||
|
||||
public constructor(state: PanelEditorState) {
|
||||
super(state);
|
||||
|
||||
this.addActivationHandler(this._activationHandler.bind(this));
|
||||
}
|
||||
|
||||
@ -90,25 +83,19 @@ export class PanelEditor extends SceneObjectBase<PanelEditorState> {
|
||||
|
||||
public commitChanges() {
|
||||
const dashboard = getDashboardSceneFor(this);
|
||||
const sourcePanel = findVizPanelByKey(dashboard.state.body, getVizPanelKeyForPanelId(this.state.panelId));
|
||||
|
||||
if (!dashboard.state.isEditing) {
|
||||
dashboard.onEnterEditMode();
|
||||
}
|
||||
|
||||
if (sourcePanel!.parent instanceof SceneGridItem) {
|
||||
sourcePanel!.parent.setState({ body: this.state.vizManager.state.panel.clone() });
|
||||
}
|
||||
this.state.vizManager.commitChanges();
|
||||
}
|
||||
}
|
||||
|
||||
export function buildPanelEditScene(panel: VizPanel): PanelEditor {
|
||||
const panelClone = panel.clone();
|
||||
const vizPanelMgr = new VizPanelManager(panelClone);
|
||||
|
||||
return new PanelEditor({
|
||||
panelId: getPanelIdForVizPanel(panel),
|
||||
optionsPane: new PanelOptionsPane({}),
|
||||
vizManager: vizPanelMgr,
|
||||
vizManager: VizPanelManager.createFor(panel),
|
||||
});
|
||||
}
|
||||
|
@ -127,13 +127,6 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
minHeight: 0,
|
||||
gap: '8px',
|
||||
}),
|
||||
controls: css({
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
padding: theme.spacing(2, 0, 2, 2),
|
||||
}),
|
||||
optionsPane: css({
|
||||
flexDirection: 'column',
|
||||
borderLeft: `1px solid ${theme.colors.border.weak}`,
|
||||
|
@ -0,0 +1,35 @@
|
||||
import { SceneDataProvider, SceneDataState, SceneObjectBase } from '@grafana/scenes';
|
||||
|
||||
export class ShareDataProvider extends SceneObjectBase<SceneDataState> implements SceneDataProvider {
|
||||
public constructor(private _source: SceneDataProvider) {
|
||||
super(_source.state);
|
||||
this.addActivationHandler(() => this.activationHandler());
|
||||
}
|
||||
|
||||
private activationHandler() {
|
||||
this._subs.add(this._source.subscribeToState((state) => this.setState({ data: state.data })));
|
||||
this.setState(this._source.state);
|
||||
}
|
||||
|
||||
public setContainerWidth(width: number) {
|
||||
if (this.state.$data && this.state.$data.setContainerWidth) {
|
||||
this.state.$data.setContainerWidth(width);
|
||||
}
|
||||
}
|
||||
|
||||
public isDataReadyToDisplay() {
|
||||
if (!this._source.isDataReadyToDisplay) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._source.isDataReadyToDisplay?.();
|
||||
}
|
||||
|
||||
public cancelQuery() {
|
||||
this._source.cancelQuery?.();
|
||||
}
|
||||
|
||||
public getResultsStream() {
|
||||
return this._source.getResultsStream!();
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import { map, of } from 'rxjs';
|
||||
|
||||
import { DataQueryRequest, DataSourceApi, DataSourceInstanceSettings, LoadingState, PanelData } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { SceneDataTransformer, SceneQueryRunner, VizPanel } from '@grafana/scenes';
|
||||
import { SceneQueryRunner, VizPanel } from '@grafana/scenes';
|
||||
import { DataQuery, DataSourceJsonData, DataSourceRef } from '@grafana/schema';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { InspectTab } from 'app/features/inspector/types';
|
||||
@ -140,7 +140,7 @@ jest.mock('@grafana/runtime', () => ({
|
||||
}));
|
||||
|
||||
describe('VizPanelManager', () => {
|
||||
describe('changePluginType', () => {
|
||||
describe('When changing plugin', () => {
|
||||
it('Should successfully change from one viz type to another', () => {
|
||||
const { vizPanelManager } = setupTest('panel-1');
|
||||
expect(vizPanelManager.state.panel.state.pluginId).toBe('timeseries');
|
||||
@ -169,7 +169,7 @@ describe('VizPanelManager', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const vizPanelManager = new VizPanelManager(vizPanel);
|
||||
const vizPanelManager = VizPanelManager.createFor(vizPanel);
|
||||
|
||||
expect(vizPanelManager.state.panel.state.fieldConfig.defaults.custom).toBe('Custom');
|
||||
expect(vizPanelManager.state.panel.state.fieldConfig.overrides).toBe(overrides);
|
||||
@ -193,7 +193,7 @@ describe('VizPanelManager', () => {
|
||||
fieldConfig: { defaults: { custom: 'Custom' }, overrides: [] },
|
||||
});
|
||||
|
||||
const vizPanelManager = new VizPanelManager(vizPanel);
|
||||
const vizPanelManager = VizPanelManager.createFor(vizPanel);
|
||||
|
||||
vizPanelManager.changePluginType('timeseries');
|
||||
//@ts-ignore
|
||||
@ -599,7 +599,6 @@ describe('VizPanelManager', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(vizPanelManager.panelData).toBeInstanceOf(SceneDataTransformer);
|
||||
expect(vizPanelManager.queryRunner.state.queries[0].panelId).toEqual(panelWithTransformations.id);
|
||||
|
||||
// Changing dashboard query to a panel with queries only
|
||||
@ -613,7 +612,6 @@ describe('VizPanelManager', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
expect(vizPanelManager.panelData).toBeInstanceOf(SceneDataTransformer);
|
||||
expect(vizPanelManager.queryRunner.state.queries[0].panelId).toBe(panelWithQueriesOnly.id);
|
||||
});
|
||||
});
|
||||
@ -624,8 +622,7 @@ const setupTest = (panelId: string) => {
|
||||
const scene = transformSaveModelToScene({ dashboard: testDashboard, meta: {} });
|
||||
const panel = findVizPanelByKey(scene, panelId)!;
|
||||
|
||||
const vizPanelManager = new VizPanelManager(panel.clone());
|
||||
|
||||
const vizPanelManager = VizPanelManager.createFor(panel);
|
||||
// The following happens on DahsboardScene activation. For the needs of this test this activation aint needed hence we hand-call it
|
||||
// @ts-expect-error
|
||||
getDashboardSrv().setCurrent(new DashboardModelCompatibilityWrapper(scene));
|
||||
|
@ -21,10 +21,12 @@ import {
|
||||
DeepPartial,
|
||||
SceneQueryRunner,
|
||||
sceneGraph,
|
||||
SceneDataProvider,
|
||||
SceneDataTransformer,
|
||||
PanelBuilders,
|
||||
SceneGridItem,
|
||||
SceneObjectRef,
|
||||
} from '@grafana/scenes';
|
||||
import { DataQuery, DataTransformerConfig } from '@grafana/schema';
|
||||
import { DataQuery, DataTransformerConfig, Panel } from '@grafana/schema';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { getPluginVersion } from 'app/features/dashboard/state/PanelModel';
|
||||
import { getLastUsedDatasourceFromStorage } from 'app/features/dashboard/utils/dashboard';
|
||||
@ -34,12 +36,21 @@ import { GrafanaQuery } from 'app/plugins/datasource/grafana/types';
|
||||
import { QueryGroupOptions } from 'app/types';
|
||||
|
||||
import { PanelTimeRange, PanelTimeRangeState } from '../scene/PanelTimeRange';
|
||||
import { gridItemToPanel } from '../serialization/transformSceneToSaveModel';
|
||||
import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils';
|
||||
|
||||
interface VizPanelManagerState extends SceneObjectState {
|
||||
panel: VizPanel;
|
||||
sourcePanel: SceneObjectRef<VizPanel>;
|
||||
datasource?: DataSourceApi;
|
||||
dsSettings?: DataSourceInstanceSettings;
|
||||
tableView?: VizPanel;
|
||||
}
|
||||
|
||||
export enum DisplayMode {
|
||||
Fill = 0,
|
||||
Fit = 1,
|
||||
Exact = 2,
|
||||
}
|
||||
|
||||
// VizPanelManager serves as an API to manipulate VizPanel state from the outside. It allows panel type, options and data manipulation.
|
||||
@ -49,18 +60,29 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
{ options: DeepPartial<{}>; fieldConfig: FieldConfigSource<DeepPartial<{}>> } | undefined
|
||||
> = {};
|
||||
|
||||
public constructor(panel: VizPanel) {
|
||||
super({ panel });
|
||||
|
||||
public constructor(state: VizPanelManagerState) {
|
||||
super(state);
|
||||
this.addActivationHandler(() => this._onActivate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Will clone the source panel and move the data provider to
|
||||
* live on the VizPanelManager level instead of the VizPanel level
|
||||
*/
|
||||
public static createFor(sourcePanel: VizPanel) {
|
||||
return new VizPanelManager({
|
||||
panel: sourcePanel.clone({ $data: undefined }),
|
||||
$data: sourcePanel.state.$data?.clone(),
|
||||
sourcePanel: sourcePanel.getRef(),
|
||||
});
|
||||
}
|
||||
|
||||
private _onActivate() {
|
||||
this.loadDataSource();
|
||||
}
|
||||
|
||||
private async loadDataSource() {
|
||||
const dataObj = this.state.panel.state.$data;
|
||||
const dataObj = this.state.$data;
|
||||
|
||||
if (!dataObj) {
|
||||
return;
|
||||
@ -96,7 +118,7 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
}
|
||||
}
|
||||
|
||||
public changePluginType(pluginType: string) {
|
||||
public changePluginType(pluginId: string) {
|
||||
const {
|
||||
options: prevOptions,
|
||||
fieldConfig: prevFieldConfig,
|
||||
@ -105,16 +127,19 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
} = sceneUtils.cloneSceneObjectState(this.state.panel.state);
|
||||
|
||||
// clear custom options
|
||||
let newFieldConfig = { ...prevFieldConfig };
|
||||
newFieldConfig.defaults = {
|
||||
...newFieldConfig.defaults,
|
||||
custom: {},
|
||||
let newFieldConfig: FieldConfigSource = {
|
||||
defaults: {
|
||||
...prevFieldConfig.defaults,
|
||||
custom: {},
|
||||
},
|
||||
overrides: filterFieldConfigOverrides(prevFieldConfig.overrides, isStandardFieldProp),
|
||||
};
|
||||
newFieldConfig.overrides = filterFieldConfigOverrides(newFieldConfig.overrides, isStandardFieldProp);
|
||||
|
||||
this._cachedPluginOptions[prevPluginId] = { options: prevOptions, fieldConfig: prevFieldConfig };
|
||||
const cachedOptions = this._cachedPluginOptions[pluginType]?.options;
|
||||
const cachedFieldConfig = this._cachedPluginOptions[pluginType]?.fieldConfig;
|
||||
|
||||
const cachedOptions = this._cachedPluginOptions[pluginId]?.options;
|
||||
const cachedFieldConfig = this._cachedPluginOptions[pluginId]?.fieldConfig;
|
||||
|
||||
if (cachedFieldConfig) {
|
||||
newFieldConfig = restoreCustomOverrideRules(newFieldConfig, cachedFieldConfig);
|
||||
}
|
||||
@ -122,19 +147,19 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
const newPanel = new VizPanel({
|
||||
options: cachedOptions ?? {},
|
||||
fieldConfig: newFieldConfig,
|
||||
pluginId: pluginType,
|
||||
pluginId: pluginId,
|
||||
...restOfOldState,
|
||||
});
|
||||
|
||||
// When changing from non-data to data panel, we need to add a new data provider
|
||||
if (!restOfOldState.$data && !config.panels[pluginType].skipDataQuery) {
|
||||
if (!this.state.$data && !config.panels[pluginId].skipDataQuery) {
|
||||
let ds = getLastUsedDatasourceFromStorage(getDashboardSceneFor(this).state.uid!)?.datasourceUid;
|
||||
|
||||
if (!ds) {
|
||||
ds = config.defaultDatasource;
|
||||
}
|
||||
|
||||
newPanel.setState({
|
||||
this.setState({
|
||||
$data: new SceneDataTransformer({
|
||||
$data: new SceneQueryRunner({
|
||||
datasource: {
|
||||
@ -153,8 +178,9 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
options: newPanel.state.options,
|
||||
fieldConfig: newPanel.state.fieldConfig,
|
||||
id: 1,
|
||||
type: pluginType,
|
||||
type: pluginId,
|
||||
};
|
||||
|
||||
const newOptions = newPlugin?.onPanelTypeChanged?.(panel, prevPluginId, prevOptions, prevFieldConfig);
|
||||
if (newOptions) {
|
||||
newPanel.onOptionsChange(newOptions, true);
|
||||
@ -208,14 +234,17 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
if (options.maxDataPoints !== dataObj.state.maxDataPoints) {
|
||||
dataObjStateUpdate.maxDataPoints = options.maxDataPoints ?? undefined;
|
||||
}
|
||||
|
||||
if (options.minInterval !== dataObj.state.minInterval && options.minInterval !== null) {
|
||||
dataObjStateUpdate.minInterval = options.minInterval;
|
||||
}
|
||||
|
||||
if (options.timeRange) {
|
||||
timeRangeObjStateUpdate.timeFrom = options.timeRange.from ?? undefined;
|
||||
timeRangeObjStateUpdate.timeShift = options.timeRange.shift ?? undefined;
|
||||
timeRangeObjStateUpdate.hideTimeOverride = options.timeRange.hide;
|
||||
}
|
||||
|
||||
if (timeRangeObj instanceof PanelTimeRange) {
|
||||
if (timeRangeObjStateUpdate.timeFrom !== undefined || timeRangeObjStateUpdate.timeShift !== undefined) {
|
||||
// update time override
|
||||
@ -264,35 +293,76 @@ export class VizPanelManager extends SceneObjectBase<VizPanelManagerState> {
|
||||
|
||||
get queryRunner(): SceneQueryRunner {
|
||||
// Panel data object is always SceneQueryRunner wrapped in a SceneDataTransformer
|
||||
const runner = getQueryRunnerFor(this.state.panel);
|
||||
const runner = getQueryRunnerFor(this);
|
||||
|
||||
if (!runner) {
|
||||
throw new Error('Query runner not found');
|
||||
}
|
||||
|
||||
return runner;
|
||||
}
|
||||
|
||||
get dataTransformer(): SceneDataTransformer {
|
||||
const provider = this.state.panel.state.$data;
|
||||
const provider = this.state.$data;
|
||||
if (!provider || !(provider instanceof SceneDataTransformer)) {
|
||||
throw new Error('Could not find SceneDataTransformer for panel');
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
get panelData(): SceneDataProvider {
|
||||
return this.state.panel.state.$data!;
|
||||
public toggleTableView() {
|
||||
if (this.state.tableView) {
|
||||
this.setState({ tableView: undefined });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
tableView: PanelBuilders.table()
|
||||
.setTitle('')
|
||||
.setOption('showTypeIcons', true)
|
||||
.setOption('showHeader', true)
|
||||
.build(),
|
||||
});
|
||||
}
|
||||
|
||||
public commitChanges() {
|
||||
const sourcePanel = this.state.sourcePanel.resolve();
|
||||
|
||||
if (sourcePanel.parent instanceof SceneGridItem) {
|
||||
sourcePanel.parent.setState({
|
||||
body: this.state.panel.clone({
|
||||
$data: this.state.$data?.clone(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used from inspect json tab to view the current persisted model
|
||||
*/
|
||||
public getPanelSaveModel(): Panel | object {
|
||||
const sourcePanel = this.state.sourcePanel.resolve();
|
||||
|
||||
if (sourcePanel.parent instanceof SceneGridItem) {
|
||||
const parentClone = sourcePanel.parent.clone({
|
||||
body: this.state.panel.clone({
|
||||
$data: this.state.$data?.clone(),
|
||||
}),
|
||||
});
|
||||
|
||||
return gridItemToPanel(parentClone);
|
||||
}
|
||||
|
||||
return { error: 'Unsupported panel parent' };
|
||||
}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<VizPanelManager>) => {
|
||||
const { panel } = model.useState();
|
||||
const { panel, tableView } = model.useState();
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<panel.Component model={panel} />
|
||||
</div>
|
||||
);
|
||||
const panelToShow = tableView ?? panel;
|
||||
|
||||
return <div className={styles.wrapper}>{<panelToShow.Component model={panelToShow} />}</div>;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
} from '@grafana/scenes';
|
||||
import { Box, Stack, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { PanelEditControls } from '../panel-edit/PanelEditControls';
|
||||
import { getDashboardSceneFor } from '../utils/utils';
|
||||
|
||||
import { DashboardLinksControls } from './DashboardLinksControls';
|
||||
@ -51,6 +52,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
||||
))}
|
||||
<Box grow={1} />
|
||||
{!editPanel && <DashboardLinksControls links={links} uid={dashboard.state.uid} />}
|
||||
{editPanel && <PanelEditControls panelEditor={editPanel} />}
|
||||
</Stack>
|
||||
{!hideTimeControls && (
|
||||
<Stack justifyContent={'flex-end'}>
|
||||
|
@ -155,12 +155,14 @@ export function getQueryRunnerFor(sceneObject: SceneObject | undefined): SceneQu
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (sceneObject.state.$data instanceof SceneQueryRunner) {
|
||||
return sceneObject.state.$data;
|
||||
const dataProvider = sceneObject.state.$data ?? sceneObject.parent?.state.$data;
|
||||
|
||||
if (dataProvider instanceof SceneQueryRunner) {
|
||||
return dataProvider;
|
||||
}
|
||||
|
||||
if (sceneObject.state.$data instanceof SceneDataTransformer) {
|
||||
return getQueryRunnerFor(sceneObject.state.$data);
|
||||
if (dataProvider instanceof SceneDataTransformer) {
|
||||
return getQueryRunnerFor(dataProvider);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
Loading…
Reference in New Issue
Block a user