mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 01:21:50 +08:00

* feat(grafana-data): expose PluginContext This is aimed to be used in the `PluginErrorBoundary` (which is a class component, and cannot use the hook.) * feat(PluginErrorBoundary): add an error boundary for plugins * feat(ExtensionsErrorBoundary): add an error boundary for extensions) * feat(Extensions/Utils): wrap components with error boundaries * feat(Plugins): wrap root plugin page with an error boundary * fix: Fallback component should always be visible for onClick() modals * review: use object arguments instead of positional ones for `renderWithPluginContext()` * review: update `wrapWithPluginContext()` to receive args as an object * refactor(AppChromeExtensionPoint): remove the error boundary We have an error boundary on the extensions-framework level now * refactor(ExtensionSidebar): remove the ErrorBoundary from the extensions This is handled on the extensions-framework level now. * test(ExtensionSidebar): add tests * chore: translation extraction * chore: prettier formatting * fix(PluginErrorBoundary): remove unnecessary type casting
66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { useObservable } from 'react-use';
|
|
|
|
import { usePluginContext } from '@grafana/data';
|
|
import { UsePluginComponentResult } from '@grafana/runtime';
|
|
|
|
import { useExposedComponentsRegistry } from './ExtensionRegistriesContext';
|
|
import * as errors from './errors';
|
|
import { log } from './logs/log';
|
|
import { useLoadAppPlugins } from './useLoadAppPlugins';
|
|
import { getExposedComponentPluginDependencies, isGrafanaDevMode, wrapWithPluginContext } from './utils';
|
|
import { isExposedComponentDependencyMissing } from './validators';
|
|
|
|
// Returns a component exposed by a plugin.
|
|
// (Exposed components can be defined in plugins by calling .exposeComponent() on the AppPlugin instance.)
|
|
export function usePluginComponent<Props extends object = {}>(id: string): UsePluginComponentResult<Props> {
|
|
const registry = useExposedComponentsRegistry();
|
|
const registryState = useObservable(registry.asObservable());
|
|
const pluginContext = usePluginContext();
|
|
const { isLoading: isLoadingAppPlugins } = useLoadAppPlugins(getExposedComponentPluginDependencies(id));
|
|
|
|
return useMemo(() => {
|
|
// For backwards compatibility we don't enable restrictions in production or when the hook is used in core Grafana.
|
|
const enableRestrictions = isGrafanaDevMode() && pluginContext;
|
|
|
|
if (isLoadingAppPlugins) {
|
|
return {
|
|
isLoading: true,
|
|
component: null,
|
|
};
|
|
}
|
|
|
|
if (!registryState?.[id]) {
|
|
return {
|
|
isLoading: false,
|
|
component: null,
|
|
};
|
|
}
|
|
|
|
const registryItem = registryState[id];
|
|
const componentLog = log.child({
|
|
title: registryItem.title,
|
|
description: registryItem.description ?? '',
|
|
pluginId: registryItem.pluginId,
|
|
});
|
|
|
|
if (enableRestrictions && isExposedComponentDependencyMissing(id, pluginContext)) {
|
|
componentLog.error(errors.EXPOSED_COMPONENT_DEPENDENCY_MISSING);
|
|
return {
|
|
isLoading: false,
|
|
component: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
isLoading: false,
|
|
component: wrapWithPluginContext({
|
|
pluginId: registryItem.pluginId,
|
|
extensionTitle: registryItem.title,
|
|
Component: registryItem.component,
|
|
log: componentLog,
|
|
}),
|
|
};
|
|
}, [id, pluginContext, registryState, isLoadingAppPlugins]);
|
|
}
|