mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PanelEditor: Adds panel instance state and a way to share this between panel & option editors (#39637)
* PanelEditor: Adds panel instance state and a way to share this between panel & option editors * Refactoring to use PanelContext
This commit is contained in:
@@ -4,20 +4,21 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
|
|||||||
import { DataFrame, InterpolateFunction, VariableSuggestionsScope, VariableSuggestion } from '../types';
|
import { DataFrame, InterpolateFunction, VariableSuggestionsScope, VariableSuggestion } from '../types';
|
||||||
import { EventBus } from '../events';
|
import { EventBus } from '../events';
|
||||||
|
|
||||||
export interface StandardEditorContext<TOptions> {
|
export interface StandardEditorContext<TOptions, TState = any> {
|
||||||
data: DataFrame[]; // All results
|
data: DataFrame[]; // All results
|
||||||
replaceVariables?: InterpolateFunction;
|
replaceVariables?: InterpolateFunction;
|
||||||
eventBus?: EventBus;
|
eventBus?: EventBus;
|
||||||
getSuggestions?: (scope?: VariableSuggestionsScope) => VariableSuggestion[];
|
getSuggestions?: (scope?: VariableSuggestionsScope) => VariableSuggestion[];
|
||||||
options?: TOptions;
|
options?: TOptions;
|
||||||
|
instanceState?: TState;
|
||||||
isOverride?: boolean;
|
isOverride?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StandardEditorProps<TValue = any, TSettings = any, TOptions = any> {
|
export interface StandardEditorProps<TValue = any, TSettings = any, TOptions = any, TState = any> {
|
||||||
value: TValue;
|
value: TValue;
|
||||||
onChange: (value?: TValue) => void;
|
onChange: (value?: TValue) => void;
|
||||||
item: StandardEditorsRegistryItem<TValue, TSettings>;
|
item: StandardEditorsRegistryItem<TValue, TSettings>;
|
||||||
context: StandardEditorContext<TOptions>;
|
context: StandardEditorContext<TOptions, TState>;
|
||||||
}
|
}
|
||||||
export interface StandardEditorsRegistryItem<TValue = any, TSettings = any> extends RegistryItem {
|
export interface StandardEditorsRegistryItem<TValue = any, TSettings = any> extends RegistryItem {
|
||||||
editor: ComponentType<StandardEditorProps<TValue, TSettings>>;
|
editor: ComponentType<StandardEditorProps<TValue, TSettings>>;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export interface FieldConfigSource<TOptions extends object = any> {
|
|||||||
overrides: ConfigOverrideRule[];
|
overrides: ConfigOverrideRule[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FieldOverrideContext extends StandardEditorContext<any> {
|
export interface FieldOverrideContext extends StandardEditorContext<any, any> {
|
||||||
field?: Field;
|
field?: Field;
|
||||||
dataFrameIndex?: number; // The index for the selected field frame
|
dataFrameIndex?: number; // The index for the selected field frame
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export interface PanelData {
|
|||||||
timeRange: TimeRange;
|
timeRange: TimeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelProps<T = any> {
|
export interface PanelProps<T = any, S = any> {
|
||||||
/** ID of the panel within the current dashboard */
|
/** ID of the panel within the current dashboard */
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ export interface PanelContext {
|
|||||||
* For example TimeSeries panel.
|
* For example TimeSeries panel.
|
||||||
*/
|
*/
|
||||||
onSplitOpen?: SplitOpen;
|
onSplitOpen?: SplitOpen;
|
||||||
|
|
||||||
|
/** For instance state that can be shared between panel & options UI */
|
||||||
|
instanceState?: any;
|
||||||
|
|
||||||
|
/** Update instance state, this is only supported in dashboard panel context currently */
|
||||||
|
onInstanceStateChange?: (state: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PanelContextRoot = React.createContext<PanelContext>({
|
export const PanelContextRoot = React.createContext<PanelContext>({
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FieldConfigSource, GrafanaTheme, PanelPlugin } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { DashboardModel, PanelModel } from '../../state';
|
|
||||||
import { useStyles } from '@grafana/ui';
|
import { useStyles } from '@grafana/ui';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
@@ -10,26 +9,17 @@ import { useSelector } from 'react-redux';
|
|||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
import { VisualizationSelectPane } from './VisualizationSelectPane';
|
import { VisualizationSelectPane } from './VisualizationSelectPane';
|
||||||
import { usePanelLatestData } from './usePanelLatestData';
|
import { usePanelLatestData } from './usePanelLatestData';
|
||||||
|
import { OptionPaneRenderProps } from './types';
|
||||||
|
|
||||||
interface Props {
|
export const OptionsPane: React.FC<OptionPaneRenderProps> = ({
|
||||||
plugin: PanelPlugin;
|
|
||||||
panel: PanelModel;
|
|
||||||
width: number;
|
|
||||||
dashboard: DashboardModel;
|
|
||||||
onFieldConfigsChange: (config: FieldConfigSource) => void;
|
|
||||||
onPanelOptionsChanged: (options: any) => void;
|
|
||||||
onPanelConfigChange: (configKey: keyof PanelModel, value: any) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OptionsPane: React.FC<Props> = ({
|
|
||||||
plugin,
|
plugin,
|
||||||
panel,
|
panel,
|
||||||
width,
|
|
||||||
onFieldConfigsChange,
|
onFieldConfigsChange,
|
||||||
onPanelOptionsChanged,
|
onPanelOptionsChanged,
|
||||||
onPanelConfigChange,
|
onPanelConfigChange,
|
||||||
dashboard,
|
dashboard,
|
||||||
}: Props) => {
|
instanceState,
|
||||||
|
}) => {
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles(getStyles);
|
||||||
const isVizPickerOpen = useSelector((state: StoreState) => state.panelEditor.isVizPickerOpen);
|
const isVizPickerOpen = useSelector((state: StoreState) => state.panelEditor.isVizPickerOpen);
|
||||||
const { data } = usePanelLatestData(panel, { withTransforms: true, withFieldConfig: false }, true);
|
const { data } = usePanelLatestData(panel, { withTransforms: true, withFieldConfig: false }, true);
|
||||||
@@ -46,6 +36,7 @@ export const OptionsPane: React.FC<Props> = ({
|
|||||||
panel={panel}
|
panel={panel}
|
||||||
dashboard={dashboard}
|
dashboard={dashboard}
|
||||||
plugin={plugin}
|
plugin={plugin}
|
||||||
|
instanceState={instanceState}
|
||||||
data={data}
|
data={data}
|
||||||
onFieldConfigsChange={onFieldConfigsChange}
|
onFieldConfigsChange={onFieldConfigsChange}
|
||||||
onPanelOptionsChanged={onPanelOptionsChanged}
|
onPanelOptionsChanged={onPanelOptionsChanged}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class OptionsPaneOptionsTestScenario {
|
|||||||
onFieldConfigsChange={this.onFieldConfigsChange}
|
onFieldConfigsChange={this.onFieldConfigsChange}
|
||||||
onPanelConfigChange={this.onPanelConfigChange}
|
onPanelConfigChange={this.onPanelConfigChange}
|
||||||
onPanelOptionsChanged={this.onPanelOptionsChanged}
|
onPanelOptionsChanged={this.onPanelOptionsChanged}
|
||||||
|
instanceState={undefined}
|
||||||
/>
|
/>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { FieldConfigSource, GrafanaTheme2, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||||
import { DashboardModel, PanelModel } from '../../state';
|
import { CustomScrollbar, FilterInput, RadioButtonGroup, useStyles2 } from '@grafana/ui';
|
||||||
import { CustomScrollbar, RadioButtonGroup, useStyles2, FilterInput } from '@grafana/ui';
|
|
||||||
import { getPanelFrameCategory } from './getPanelFrameOptions';
|
import { getPanelFrameCategory } from './getPanelFrameOptions';
|
||||||
import { getVizualizationOptions } from './getVizualizationOptions';
|
import { getVizualizationOptions } from './getVizualizationOptions';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
@@ -13,18 +12,9 @@ import { AngularPanelOptions } from './AngularPanelOptions';
|
|||||||
import { getRecentOptions } from './state/getRecentOptions';
|
import { getRecentOptions } from './state/getRecentOptions';
|
||||||
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
import { isPanelModelLibraryPanel } from '../../../library-panels/guard';
|
||||||
import { getLibraryPanelOptionsCategory } from './getLibraryPanelOptions';
|
import { getLibraryPanelOptionsCategory } from './getLibraryPanelOptions';
|
||||||
|
import { OptionPaneRenderProps } from './types';
|
||||||
|
|
||||||
interface Props {
|
export const OptionsPaneOptions: React.FC<OptionPaneRenderProps> = (props) => {
|
||||||
plugin: PanelPlugin;
|
|
||||||
panel: PanelModel;
|
|
||||||
dashboard: DashboardModel;
|
|
||||||
data?: PanelData;
|
|
||||||
onFieldConfigsChange: (config: FieldConfigSource) => void;
|
|
||||||
onPanelOptionsChanged: (options: any) => void;
|
|
||||||
onPanelConfigChange: (configKey: keyof PanelModel, value: any) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const OptionsPaneOptions: React.FC<Props> = (props) => {
|
|
||||||
const { plugin, dashboard, panel } = props;
|
const { plugin, dashboard, panel } = props;
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [listMode, setListMode] = useState(OptionFilter.All);
|
const [listMode, setListMode] = useState(OptionFilter.All);
|
||||||
@@ -39,7 +29,7 @@ export const OptionsPaneOptions: React.FC<Props> = (props) => {
|
|||||||
],
|
],
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[panel.configRev, props.data]
|
[panel.configRev, props.data, props.instanceState]
|
||||||
);
|
);
|
||||||
|
|
||||||
const mainBoxElements: React.ReactNode[] = [];
|
const mainBoxElements: React.ReactNode[] = [];
|
||||||
|
|||||||
@@ -64,11 +64,12 @@ interface OwnProps {
|
|||||||
|
|
||||||
const mapStateToProps = (state: StoreState) => {
|
const mapStateToProps = (state: StoreState) => {
|
||||||
const panel = state.panelEditor.getPanel();
|
const panel = state.panelEditor.getPanel();
|
||||||
const { plugin } = getPanelStateById(state.dashboard, panel.id);
|
const { plugin, instanceState } = getPanelStateById(state.dashboard, panel.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugin: plugin,
|
plugin: plugin,
|
||||||
panel,
|
panel,
|
||||||
|
instanceState,
|
||||||
initDone: state.panelEditor.initDone,
|
initDone: state.panelEditor.initDone,
|
||||||
uiState: state.panelEditor.ui,
|
uiState: state.panelEditor.ui,
|
||||||
tableViewEnabled: state.panelEditor.tableViewEnabled,
|
tableViewEnabled: state.panelEditor.tableViewEnabled,
|
||||||
@@ -395,12 +396,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderOptionsPane() {
|
renderOptionsPane() {
|
||||||
const { plugin, dashboard, panel, uiState } = this.props;
|
const { plugin, dashboard, panel, instanceState } = this.props;
|
||||||
|
|
||||||
const rightPaneSize =
|
|
||||||
uiState.rightPaneSize <= 1
|
|
||||||
? (uiState.rightPaneSize as number) * window.innerWidth
|
|
||||||
: (uiState.rightPaneSize as number);
|
|
||||||
|
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
return <div />;
|
return <div />;
|
||||||
@@ -411,7 +407,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
|||||||
plugin={plugin}
|
plugin={plugin}
|
||||||
dashboard={dashboard}
|
dashboard={dashboard}
|
||||||
panel={panel}
|
panel={panel}
|
||||||
width={rightPaneSize}
|
instanceState={instanceState}
|
||||||
onFieldConfigsChange={this.onFieldConfigChange}
|
onFieldConfigsChange={this.onFieldConfigChange}
|
||||||
onPanelOptionsChanged={this.onPanelOptionsChanged}
|
onPanelOptionsChanged={this.onPanelOptionsChanged}
|
||||||
onPanelConfigChange={this.onPanelConfigChanged}
|
onPanelConfigChange={this.onPanelConfigChanged}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import { OptionsPaneCategoryDescriptor } from './OptionsPaneCategoryDescriptor';
|
|||||||
type categoryGetter = (categoryNames?: string[]) => OptionsPaneCategoryDescriptor;
|
type categoryGetter = (categoryNames?: string[]) => OptionsPaneCategoryDescriptor;
|
||||||
|
|
||||||
export function getVizualizationOptions(props: OptionPaneRenderProps): OptionsPaneCategoryDescriptor[] {
|
export function getVizualizationOptions(props: OptionPaneRenderProps): OptionsPaneCategoryDescriptor[] {
|
||||||
const { plugin, panel, onPanelOptionsChanged, onFieldConfigsChange, data, dashboard } = props;
|
const { plugin, panel, onPanelOptionsChanged, onFieldConfigsChange, data, dashboard, instanceState } = props;
|
||||||
const currentOptions = panel.getOptions();
|
const currentOptions = panel.getOptions();
|
||||||
const currentFieldConfig = panel.fieldConfig;
|
const currentFieldConfig = panel.fieldConfig;
|
||||||
const categoryIndex: Record<string, OptionsPaneCategoryDescriptor> = {};
|
const categoryIndex: Record<string, OptionsPaneCategoryDescriptor> = {};
|
||||||
|
|
||||||
const context: StandardEditorContext<any> = {
|
const context: StandardEditorContext<any, any> = {
|
||||||
data: data?.series || [],
|
data: data?.series || [],
|
||||||
replaceVariables: panel.replaceVariables,
|
replaceVariables: panel.replaceVariables,
|
||||||
options: currentOptions,
|
options: currentOptions,
|
||||||
@@ -23,6 +23,7 @@ export function getVizualizationOptions(props: OptionPaneRenderProps): OptionsPa
|
|||||||
getSuggestions: (scope?: VariableSuggestionsScope) => {
|
getSuggestions: (scope?: VariableSuggestionsScope) => {
|
||||||
return data ? getDataLinksVariableSuggestions(data.series, scope) : [];
|
return data ? getDataLinksVariableSuggestions(data.series, scope) : [];
|
||||||
},
|
},
|
||||||
|
instanceState,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOptionsPaneCategory = (categoryNames?: string[]): OptionsPaneCategoryDescriptor => {
|
const getOptionsPaneCategory = (categoryNames?: string[]): OptionsPaneCategoryDescriptor => {
|
||||||
@@ -109,7 +110,7 @@ export function fillOptionsPaneItems(
|
|||||||
optionEditors: PanelOptionsEditorItem[],
|
optionEditors: PanelOptionsEditorItem[],
|
||||||
getOptionsPaneCategory: categoryGetter,
|
getOptionsPaneCategory: categoryGetter,
|
||||||
onValueChanged: (path: string, value: any) => void,
|
onValueChanged: (path: string, value: any) => void,
|
||||||
context: StandardEditorContext<any>
|
context: StandardEditorContext<any, any>
|
||||||
) {
|
) {
|
||||||
for (const pluginOption of optionEditors) {
|
for (const pluginOption of optionEditors) {
|
||||||
if (pluginOption.showIf && !pluginOption.showIf(context.options, context.data)) {
|
if (pluginOption.showIf && !pluginOption.showIf(context.options, context.data)) {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export interface OptionPaneRenderProps {
|
|||||||
plugin: PanelPlugin;
|
plugin: PanelPlugin;
|
||||||
data?: PanelData;
|
data?: PanelData;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
|
instanceState: any;
|
||||||
onPanelConfigChange: (configKey: keyof PanelModel, value: any) => void;
|
onPanelConfigChange: (configKey: keyof PanelModel, value: any) => void;
|
||||||
onPanelOptionsChanged: (options: any) => void;
|
onPanelOptionsChanged: (options: any) => void;
|
||||||
onFieldConfigsChange: (config: FieldConfigSource) => void;
|
onFieldConfigsChange: (config: FieldConfigSource) => void;
|
||||||
|
|||||||
@@ -36,10 +36,13 @@ const mapStateToProps = (state: StoreState, props: OwnProps) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
plugin: panelState.plugin,
|
plugin: panelState.plugin,
|
||||||
|
instanceState: panelState.instanceState,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = { initDashboardPanel };
|
const mapDispatchToProps = {
|
||||||
|
initDashboardPanel,
|
||||||
|
};
|
||||||
|
|
||||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../annota
|
|||||||
import { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
|
import { getDashboardQueryRunner } from '../../query/state/DashboardQueryRunner/DashboardQueryRunner';
|
||||||
import { liveTimer } from './liveTimer';
|
import { liveTimer } from './liveTimer';
|
||||||
import { isSoloRoute } from '../../../routes/utils';
|
import { isSoloRoute } from '../../../routes/utils';
|
||||||
|
import { setPanelInstanceState } from '../state/reducers';
|
||||||
|
import { store } from 'app/store/store';
|
||||||
|
|
||||||
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
||||||
|
|
||||||
@@ -84,11 +86,24 @@ export class PanelChrome extends Component<Props, State> {
|
|||||||
onAnnotationUpdate: this.onAnnotationUpdate,
|
onAnnotationUpdate: this.onAnnotationUpdate,
|
||||||
onAnnotationDelete: this.onAnnotationDelete,
|
onAnnotationDelete: this.onAnnotationDelete,
|
||||||
canAddAnnotations: () => Boolean(props.dashboard.meta.canEdit || props.dashboard.meta.canMakeEditable),
|
canAddAnnotations: () => Boolean(props.dashboard.meta.canEdit || props.dashboard.meta.canMakeEditable),
|
||||||
|
onInstanceStateChange: this.onInstanceStateChange,
|
||||||
},
|
},
|
||||||
data: this.getInitialPanelDataState(),
|
data: this.getInitialPanelDataState(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onInstanceStateChange = (value: any) => {
|
||||||
|
this.setState({
|
||||||
|
context: {
|
||||||
|
...this.state.context,
|
||||||
|
instanceState: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set redux panel state so panel options can get notified
|
||||||
|
store.dispatch(setPanelInstanceState({ panelId: this.props.panel.id, value }));
|
||||||
|
};
|
||||||
|
|
||||||
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));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ const dashbardSlice = createSlice({
|
|||||||
cleanUpEditPanel: (state) => {
|
cleanUpEditPanel: (state) => {
|
||||||
delete state.panels[EDIT_PANEL_ID];
|
delete state.panels[EDIT_PANEL_ID];
|
||||||
},
|
},
|
||||||
|
setPanelInstanceState: (state, action: PayloadAction<SetPanelInstanceStatePayload>) => {
|
||||||
|
updatePanelState(state, action.payload.panelId, { instanceState: action.payload.value });
|
||||||
|
},
|
||||||
setPanelAngularComponent: (state, action: PayloadAction<SetPanelAngularComponentPayload>) => {
|
setPanelAngularComponent: (state, action: PayloadAction<SetPanelAngularComponentPayload>) => {
|
||||||
updatePanelState(state, action.payload.panelId, { angularComponent: action.payload.angularComponent });
|
updatePanelState(state, action.payload.panelId, { angularComponent: action.payload.angularComponent });
|
||||||
},
|
},
|
||||||
@@ -105,6 +108,11 @@ export interface SetPanelAngularComponentPayload {
|
|||||||
angularComponent: AngularComponent | null;
|
angularComponent: AngularComponent | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetPanelInstanceStatePayload {
|
||||||
|
panelId: number;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
loadDashboardPermissions,
|
loadDashboardPermissions,
|
||||||
dashboardInitFetching,
|
dashboardInitFetching,
|
||||||
@@ -119,6 +127,7 @@ export const {
|
|||||||
addPanel,
|
addPanel,
|
||||||
cleanUpEditPanel,
|
cleanUpEditPanel,
|
||||||
setPanelAngularComponent,
|
setPanelAngularComponent,
|
||||||
|
setPanelInstanceState,
|
||||||
} = dashbardSlice.actions;
|
} = dashbardSlice.actions;
|
||||||
|
|
||||||
export const dashboardReducer = dashbardSlice.reducer;
|
export const dashboardReducer = dashbardSlice.reducer;
|
||||||
|
|||||||
@@ -5,19 +5,26 @@ import { DebugPanelOptions, DebugMode } from './types';
|
|||||||
import { EventBusLoggerPanel } from './EventBusLogger';
|
import { EventBusLoggerPanel } from './EventBusLogger';
|
||||||
import { RenderInfoViewer } from './RenderInfoViewer';
|
import { RenderInfoViewer } from './RenderInfoViewer';
|
||||||
import { CursorView } from './CursorView';
|
import { CursorView } from './CursorView';
|
||||||
|
import { StateView } from './StateView';
|
||||||
|
|
||||||
type Props = PanelProps<DebugPanelOptions>;
|
type Props = PanelProps<DebugPanelOptions>;
|
||||||
|
|
||||||
export class DebugPanel extends Component<Props> {
|
export class DebugPanel extends Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
|
|
||||||
if (options.mode === DebugMode.Events) {
|
if (options.mode === DebugMode.Events) {
|
||||||
return <EventBusLoggerPanel eventBus={this.props.eventBus} />;
|
return <EventBusLoggerPanel eventBus={this.props.eventBus} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.mode === DebugMode.Cursor) {
|
if (options.mode === DebugMode.Cursor) {
|
||||||
return <CursorView eventBus={this.props.eventBus} />;
|
return <CursorView eventBus={this.props.eventBus} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.mode === DebugMode.State) {
|
||||||
|
return <StateView {...this.props} />;
|
||||||
|
}
|
||||||
|
|
||||||
return <RenderInfoViewer {...this.props} />;
|
return <RenderInfoViewer {...this.props} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
public/app/plugins/panel/debug/StateView.tsx
Normal file
24
public/app/plugins/panel/debug/StateView.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import React, { FormEvent } from 'react';
|
||||||
|
import { PanelOptionsEditorProps, PanelProps } from '@grafana/data';
|
||||||
|
import { Field, Input, usePanelContext } from '@grafana/ui';
|
||||||
|
import { DebugPanelOptions } from './types';
|
||||||
|
|
||||||
|
export function StateView(props: PanelProps<DebugPanelOptions>) {
|
||||||
|
const context = usePanelContext();
|
||||||
|
|
||||||
|
const onChangeName = (e: FormEvent<HTMLInputElement>) => {
|
||||||
|
context.onInstanceStateChange!({
|
||||||
|
name: e.currentTarget.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Field label="State name">
|
||||||
|
<Input value={context.instanceState?.name ?? ''} onChange={onChangeName} />
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StateViewEditor({ value, context, onChange, item }: PanelOptionsEditorProps<string>) {
|
||||||
|
return <div>Current value: {context.instanceState?.name} </div>;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PanelPlugin } from '@grafana/data';
|
import { PanelPlugin } from '@grafana/data';
|
||||||
import { DebugPanel } from './DebugPanel';
|
import { DebugPanel } from './DebugPanel';
|
||||||
|
import { StateViewEditor } from './StateView';
|
||||||
import { DebugMode, DebugPanelOptions } from './types';
|
import { DebugMode, DebugPanelOptions } from './types';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<DebugPanelOptions>(DebugPanel).useFieldConfig().setPanelOptions((builder) => {
|
export const plugin = new PanelPlugin<DebugPanelOptions>(DebugPanel).useFieldConfig().setPanelOptions((builder) => {
|
||||||
@@ -13,9 +14,18 @@ export const plugin = new PanelPlugin<DebugPanelOptions>(DebugPanel).useFieldCon
|
|||||||
{ label: 'Render', value: DebugMode.Render },
|
{ label: 'Render', value: DebugMode.Render },
|
||||||
{ label: 'Events', value: DebugMode.Events },
|
{ label: 'Events', value: DebugMode.Events },
|
||||||
{ label: 'Cursor', value: DebugMode.Cursor },
|
{ label: 'Cursor', value: DebugMode.Cursor },
|
||||||
|
{ label: 'Share state', value: DebugMode.State },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
.addCustomEditor({
|
||||||
|
id: 'stateView',
|
||||||
|
path: 'stateView',
|
||||||
|
name: 'State view',
|
||||||
|
defaultValue: '',
|
||||||
|
showIf: ({ mode }) => mode === DebugMode.State,
|
||||||
|
editor: StateViewEditor,
|
||||||
|
})
|
||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
path: 'counters.render',
|
path: 'counters.render',
|
||||||
name: 'Render Count',
|
name: 'Render Count',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export enum DebugMode {
|
|||||||
Render = 'render',
|
Render = 'render',
|
||||||
Events = 'events',
|
Events = 'events',
|
||||||
Cursor = 'cursor',
|
Cursor = 'cursor',
|
||||||
|
State = 'State',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DebugPanelOptions {
|
export interface DebugPanelOptions {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export interface PanelState {
|
|||||||
pluginId: string;
|
pluginId: string;
|
||||||
plugin?: PanelPlugin;
|
plugin?: PanelPlugin;
|
||||||
angularComponent?: AngularComponent | null;
|
angularComponent?: AngularComponent | null;
|
||||||
|
instanceState?: any | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DashboardState {
|
export interface DashboardState {
|
||||||
|
|||||||
Reference in New Issue
Block a user