PanelContext: Add functionality to update data from panel (#66993)

This commit is contained in:
Torkel Ödegaard 2023-04-25 14:55:50 +02:00 committed by GitHub
parent 6ca7d8fa37
commit de18ed6659
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 2 deletions

View File

@ -8,6 +8,7 @@ import {
ThresholdsConfig, ThresholdsConfig,
SplitOpen, SplitOpen,
CoreApp, CoreApp,
DataFrame,
} from '@grafana/data'; } from '@grafana/data';
import { AdHocFilterItem } from '../Table/types'; import { AdHocFilterItem } from '../Table/types';
@ -82,6 +83,12 @@ export interface PanelContext {
* Called when a panel is changing the sort order of the legends. * Called when a panel is changing the sort order of the legends.
*/ */
onToggleLegendSort?: (sortBy: string) => void; onToggleLegendSort?: (sortBy: string) => void;
/**
* Optional, only some contexts support this. This action can be cancelled by user which will result
* in a the Promise resolving to a false value.
*/
onUpdateData?: (frames: DataFrame[]) => Promise<boolean>;
} }
export const PanelContextRoot = React.createContext<PanelContext>({ export const PanelContextRoot = React.createContext<PanelContext>({

View File

@ -8,6 +8,7 @@ import {
AnnotationEventUIModel, AnnotationEventUIModel,
CoreApp, CoreApp,
DashboardCursorSync, DashboardCursorSync,
DataFrame,
EventFilterOptions, EventFilterOptions,
FieldConfigSource, FieldConfigSource,
getDataSourceRef, getDataSourceRef,
@ -42,6 +43,7 @@ import { InspectTab } from 'app/features/inspector/types';
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { applyFilterFromTable } from 'app/features/variables/adhoc/actions'; import { applyFilterFromTable } from 'app/features/variables/adhoc/actions';
import { onUpdatePanelSnapshotData } from 'app/plugins/datasource/grafana/utils';
import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory'; import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory';
import { dispatch } from 'app/store/store'; import { dispatch } from 'app/store/store';
import { RenderEvent } from 'app/types/events'; import { RenderEvent } from 'app/types/events';
@ -116,6 +118,7 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
canEditAnnotations: props.dashboard.canEditAnnotations.bind(props.dashboard), canEditAnnotations: props.dashboard.canEditAnnotations.bind(props.dashboard),
canDeleteAnnotations: props.dashboard.canDeleteAnnotations.bind(props.dashboard), canDeleteAnnotations: props.dashboard.canDeleteAnnotations.bind(props.dashboard),
onAddAdHocFilter: this.onAddAdHocFilter, onAddAdHocFilter: this.onAddAdHocFilter,
onUpdateData: this.onUpdateData,
}, },
data: this.getInitialPanelDataState(), data: this.getInitialPanelDataState(),
}; };
@ -146,6 +149,10 @@ export class PanelStateWrapper extends PureComponent<Props, State> {
return CoreApp.Dashboard; return CoreApp.Dashboard;
} }
onUpdateData = (frames: DataFrame[]): Promise<boolean> => {
return onUpdatePanelSnapshotData(this.props.panel, frames);
};
onSeriesColorChange = (label: string, color: string) => { onSeriesColorChange = (label: string, color: string) => {
this.onFieldConfigChange(changeSeriesColorConfigFactory(label, color, this.props.panel.fieldConfig)); this.onFieldConfigChange(changeSeriesColorConfigFactory(label, color, this.props.panel.fieldConfig));
}; };

View File

@ -99,8 +99,6 @@ describe('DashboardLoader', () => {
const loader = new DashboardLoader({}); const loader = new DashboardLoader({});
await loader.load('fake-dash'); await loader.load('fake-dash');
expect(loader.state.dashboard).toBeInstanceOf(DashboardScene); expect(loader.state.dashboard).toBeInstanceOf(DashboardScene);
// @ts-expect-error - private
expect(loader.state.dashboard?.urlSyncManager).toBeDefined();
expect(loader.state.isLoading).toBe(false); expect(loader.state.isLoading).toBe(false);
}); });
}); });

View File

@ -0,0 +1,56 @@
import { DataFrame, DataFrameJSON, dataFrameToJSON } from '@grafana/data';
import appEvents from 'app/core/app_events';
import { GRAFANA_DATASOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
import { PanelModel } from 'app/features/dashboard/state';
import { ShowConfirmModalEvent } from 'app/types/events';
import { GrafanaQuery, GrafanaQueryType } from './types';
/**
* Will show a confirm modal if the current panel does not have a snapshot query.
* If the confirm modal is shown, and the user aborts the promise will resolve with a false value,
* otherwise it will resolve with a true value.
*/
export function onUpdatePanelSnapshotData(panel: PanelModel, frames: DataFrame[]): Promise<boolean> {
return new Promise<boolean>((resolve) => {
if (panel.datasource?.uid === GRAFANA_DATASOURCE_NAME) {
updateSnapshotData(frames, panel);
resolve(true);
return;
}
appEvents.publish(
new ShowConfirmModalEvent({
title: 'Change to panel embedded data',
text: 'If you want to change the data shown in this panel Grafana will need to remove the panels current query and replace it with a snapshot of the current data. This enabled you to edit the data',
yesText: 'Continue',
icon: 'pen',
onConfirm: () => {
updateSnapshotData(frames, panel);
resolve(true);
},
onDismiss: () => {
resolve(false);
},
})
);
});
}
function updateSnapshotData(frames: DataFrame[], panel: PanelModel) {
const snapshot: DataFrameJSON[] = frames.map((f) => dataFrameToJSON(f));
const query: GrafanaQuery = {
refId: 'A',
queryType: GrafanaQueryType.Snapshot,
snapshot,
datasource: { uid: GRAFANA_DATASOURCE_NAME },
};
panel.updateQueries({
dataSource: { uid: GRAFANA_DATASOURCE_NAME },
queries: [query],
});
panel.refresh();
}