PanelRenderer.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import React, { useState, useMemo, useEffect, useRef } from 'react';
  2. import { useAsync } from 'react-use';
  3. import { applyFieldOverrides, FieldConfigSource, getTimeZone, PanelData, PanelPlugin } from '@grafana/data';
  4. import { PanelRendererProps } from '@grafana/runtime';
  5. import { ErrorBoundaryAlert, useTheme2 } from '@grafana/ui';
  6. import { appEvents } from 'app/core/core';
  7. import { getPanelOptionsWithDefaults, OptionDefaults } from '../../dashboard/state/getPanelOptionsWithDefaults';
  8. import { importPanelPlugin } from '../../plugins/importPanelPlugin';
  9. const defaultFieldConfig = { defaults: {}, overrides: [] };
  10. export function PanelRenderer<P extends object = any, F extends object = any>(props: PanelRendererProps<P, F>) {
  11. const {
  12. pluginId,
  13. data,
  14. timeZone = getTimeZone(),
  15. options = {},
  16. width,
  17. height,
  18. title,
  19. onOptionsChange = () => {},
  20. onChangeTimeRange = () => {},
  21. fieldConfig: externalFieldConfig = defaultFieldConfig,
  22. } = props;
  23. const [localFieldConfig, setFieldConfig] = useState(externalFieldConfig);
  24. const { value: plugin, error, loading } = useAsync(() => importPanelPlugin(pluginId), [pluginId]);
  25. const optionsWithDefaults = useOptionDefaults(plugin, options, localFieldConfig);
  26. const dataWithOverrides = useFieldOverrides(plugin, optionsWithDefaults, data, timeZone);
  27. useEffect(() => {
  28. setFieldConfig((lfc) => ({ ...lfc, ...externalFieldConfig }));
  29. }, [externalFieldConfig]);
  30. if (error) {
  31. return <div>Failed to load plugin: {error.message}</div>;
  32. }
  33. if (pluginIsLoading(loading, plugin, pluginId)) {
  34. return <div>Loading plugin panel...</div>;
  35. }
  36. if (!plugin || !plugin.panel) {
  37. return <div>Seems like the plugin you are trying to load does not have a panel component.</div>;
  38. }
  39. if (!dataWithOverrides) {
  40. return <div>No panel data</div>;
  41. }
  42. const PanelComponent = plugin.panel;
  43. return (
  44. <ErrorBoundaryAlert dependencies={[plugin, data]}>
  45. <PanelComponent
  46. id={1}
  47. data={dataWithOverrides}
  48. title={title}
  49. timeRange={dataWithOverrides.timeRange}
  50. timeZone={timeZone}
  51. options={optionsWithDefaults!.options}
  52. fieldConfig={localFieldConfig}
  53. transparent={false}
  54. width={width}
  55. height={height}
  56. renderCounter={0}
  57. replaceVariables={(str: string) => str}
  58. onOptionsChange={onOptionsChange}
  59. onFieldConfigChange={setFieldConfig}
  60. onChangeTimeRange={onChangeTimeRange}
  61. eventBus={appEvents}
  62. />
  63. </ErrorBoundaryAlert>
  64. );
  65. }
  66. function useOptionDefaults<P extends object = any, F extends object = any>(
  67. plugin: PanelPlugin | undefined,
  68. options: P,
  69. fieldConfig: FieldConfigSource<F>
  70. ): OptionDefaults | undefined {
  71. return useMemo(() => {
  72. if (!plugin) {
  73. return;
  74. }
  75. return getPanelOptionsWithDefaults({
  76. plugin,
  77. currentOptions: options,
  78. currentFieldConfig: fieldConfig,
  79. isAfterPluginChange: false,
  80. });
  81. }, [plugin, fieldConfig, options]);
  82. }
  83. function useFieldOverrides(
  84. plugin: PanelPlugin | undefined,
  85. defaultOptions: OptionDefaults | undefined,
  86. data: PanelData | undefined,
  87. timeZone: string
  88. ): PanelData | undefined {
  89. const fieldConfig = defaultOptions?.fieldConfig;
  90. const series = data?.series;
  91. const fieldConfigRegistry = plugin?.fieldConfigRegistry;
  92. const theme = useTheme2();
  93. const structureRev = useRef(0);
  94. return useMemo(() => {
  95. if (!fieldConfigRegistry || !fieldConfig || !data) {
  96. return;
  97. }
  98. structureRev.current = structureRev.current + 1;
  99. return {
  100. ...data,
  101. series: applyFieldOverrides({
  102. data: series,
  103. fieldConfig,
  104. fieldConfigRegistry,
  105. replaceVariables: (str: string) => str,
  106. theme,
  107. timeZone,
  108. }),
  109. structureRev: structureRev.current,
  110. };
  111. }, [fieldConfigRegistry, fieldConfig, data, series, timeZone, theme]);
  112. }
  113. function pluginIsLoading(loading: boolean, plugin: PanelPlugin<any, any> | undefined, pluginId: string) {
  114. return loading || plugin?.meta.id !== pluginId;
  115. }