diff --git a/packages/grafana-runtime/src/components/PanelRenderer.tsx b/packages/grafana-runtime/src/components/PanelRenderer.tsx
index 1d178836483..23c140408a4 100644
--- a/packages/grafana-runtime/src/components/PanelRenderer.tsx
+++ b/packages/grafana-runtime/src/components/PanelRenderer.tsx
@@ -13,10 +13,11 @@ export interface PanelRendererProps
;
options?: P;
onOptionsChange?: (options: P) => void;
onChangeTimeRange?: (timeRange: AbsoluteTimeRange) => void;
+ fieldConfig?: FieldConfigSource;
+ onFieldConfigChange?: (config: FieldConfigSource) => void;
timeZone?: string;
width: number;
height: number;
diff --git a/public/app/features/panel/PanelRenderer.tsx b/public/app/features/panel/PanelRenderer.tsx
index 03d4c978c0a..14288099cbe 100644
--- a/public/app/features/panel/PanelRenderer.tsx
+++ b/public/app/features/panel/PanelRenderer.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useMemo } from 'react';
+import React, { useState, useMemo, useRef, useCallback } from 'react';
import { applyFieldOverrides, FieldConfigSource, getTimeZone, PanelData, PanelPlugin } from '@grafana/data';
import { PanelRendererProps } from '@grafana/runtime';
import { appEvents } from 'app/core/core';
@@ -18,10 +18,9 @@ export function PanelRenderer(pr
title,
onOptionsChange = () => {},
onChangeTimeRange = () => {},
- fieldConfig: config = { defaults: {}, overrides: [] },
} = props;
- const [fieldConfig, setFieldConfig] = useState(config);
+ const [fieldConfig, setFieldConfig] = useFieldConfigState(props);
const { value: plugin, error, loading } = useAsync(() => importPanelPlugin(pluginId), [pluginId]);
const optionsWithDefaults = useOptionDefaults(plugin, options, fieldConfig);
const dataWithOverrides = useFieldOverrides(plugin, optionsWithDefaults, data, timeZone);
@@ -118,3 +117,35 @@ function useFieldOverrides(
function pluginIsLoading(loading: boolean, plugin: PanelPlugin | undefined, pluginId: string) {
return loading || plugin?.meta.id !== pluginId;
}
+
+function useFieldConfigState(props: PanelRendererProps): [FieldConfigSource, (config: FieldConfigSource) => void] {
+ const {
+ onFieldConfigChange,
+ fieldConfig = {
+ defaults: {},
+ overrides: [],
+ },
+ } = props;
+
+ // First render will detect if the PanelRenderer will manage the
+ // field config state internally or externally by the consuming
+ // component. This will also prevent the way of managing state to
+ // change during the components life cycle.
+ const isManagedInternally = useRef(() => !!onFieldConfigChange);
+ const [internalConfig, setInternalConfig] = useState(fieldConfig);
+
+ const setExternalConfig = useCallback(
+ (config: FieldConfigSource) => {
+ if (!onFieldConfigChange) {
+ return;
+ }
+ onFieldConfigChange(config);
+ },
+ [onFieldConfigChange]
+ );
+
+ if (isManagedInternally) {
+ return [internalConfig, setInternalConfig];
+ }
+ return [fieldConfig, setExternalConfig];
+}