diff --git a/packages/app-utils/src/plugins/PluginUtils.tsx b/packages/app-utils/src/plugins/PluginUtils.tsx index 1e49beb646..56869d4061 100644 --- a/packages/app-utils/src/plugins/PluginUtils.tsx +++ b/packages/app-utils/src/plugins/PluginUtils.tsx @@ -16,6 +16,14 @@ export interface PluginModule {} export type PluginModuleMap = Map; +export type PluginManifestPluginInfo = { + name: string; + main: string; + version: string; +}; + +export type PluginManifest = { plugins: PluginManifestPluginInfo[] }; + /** * Load a component plugin from the server. * @param baseURL Base URL of the plugin server @@ -53,7 +61,9 @@ export function loadComponentPlugin( * @param pluginUrl The URL of the plugin to load * @returns The loaded module */ -export async function loadModulePlugin(pluginUrl: string): Promise { +export async function loadModulePlugin( + pluginUrl: string +): Promise { const myModule = await loadRemoteModule(pluginUrl); return myModule; } @@ -63,9 +73,7 @@ export async function loadModulePlugin(pluginUrl: string): Promise { * @param jsonUrl The URL of the JSON file to load * @returns The JSON object of the manifest file */ -export async function loadJson( - jsonUrl: string -): Promise<{ plugins: { name: string; main: string }[] }> { +export async function loadJson(jsonUrl: string): Promise { const res = await fetch(jsonUrl); if (!res.ok) { throw new Error(res.statusText); @@ -94,7 +102,7 @@ export async function loadModulePlugins( } log.debug('Plugin manifest loaded:', manifest); - const pluginPromises: Promise[] = []; + const pluginPromises: Promise[] = []; for (let i = 0; i < manifest.plugins.length; i += 1) { const { name, main } = manifest.plugins[i]; const pluginMainUrl = `${modulePluginsUrl}/${name}/${main}`; @@ -107,7 +115,7 @@ export async function loadModulePlugins( const module = pluginModules[i]; const { name } = manifest.plugins[i]; if (module.status === 'fulfilled') { - pluginMap.set(name, module.value as PluginModule); + pluginMap.set(name, module.value); } else { log.error(`Unable to load plugin ${name}`, module.reason); } diff --git a/packages/code-studio/tsconfig.json b/packages/code-studio/tsconfig.json index 89eae0923e..862ade28cb 100644 --- a/packages/code-studio/tsconfig.json +++ b/packages/code-studio/tsconfig.json @@ -9,6 +9,7 @@ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], "exclude": ["node_modules", "src/**/*.test.*", "src/**/__mocks__/*"], "references": [ + { "path": "../app-utils" }, { "path": "../auth-plugins" }, { "path": "../chart" }, { "path": "../components" }, diff --git a/packages/dashboard/src/Dashboard.tsx b/packages/dashboard/src/Dashboard.tsx index b5f23fd5fd..ab5f228b63 100644 --- a/packages/dashboard/src/Dashboard.tsx +++ b/packages/dashboard/src/Dashboard.tsx @@ -1,4 +1,5 @@ import React, { + ComponentType, ForwardRefExoticComponent, RefAttributes, useEffect, @@ -40,6 +41,9 @@ export type DashboardProps = { >; hydrate?: PanelHydrateFunction; dehydrate?: PanelDehydrateFunction; + + /** Component to wrap each panel with */ + panelWrapper?: ComponentType; }; export function Dashboard({ @@ -54,6 +58,7 @@ export function Dashboard({ fallbackComponent = PanelPlaceholder, hydrate, dehydrate, + panelWrapper, }: DashboardProps): JSX.Element { const layoutElement = useRef(null); const [isInitialized, setIsInitialized] = useState(false); @@ -140,6 +145,7 @@ export function Dashboard({ onLayoutInitialized={onLayoutInitialized} hydrate={hydrate} dehydrate={dehydrate} + panelWrapper={panelWrapper} > {children} diff --git a/packages/dashboard/src/DashboardLayout.tsx b/packages/dashboard/src/DashboardLayout.tsx index 7cb3f34b46..2873a5fdb1 100644 --- a/packages/dashboard/src/DashboardLayout.tsx +++ b/packages/dashboard/src/DashboardLayout.tsx @@ -1,4 +1,5 @@ import React, { + ComponentType, ReactElement, useCallback, useEffect, @@ -63,6 +64,9 @@ interface DashboardLayoutProps { data?: DashboardData; children?: React.ReactNode | React.ReactNode[]; emptyDashboard?: React.ReactNode; + + /** Component to wrap each panel with */ + panelWrapper?: ComponentType; } /** @@ -78,6 +82,8 @@ export function DashboardLayout({ onLayoutInitialized = DEFAULT_CALLBACK, hydrate = hydrateDefault, dehydrate = dehydrateDefault, + // eslint-disable-next-line react/jsx-no-useless-fragment + panelWrapper = ({ children: panelChildren }) => <>{panelChildren}, }: DashboardLayoutProps): JSX.Element { const dispatch = useDispatch(); const data = @@ -117,6 +123,8 @@ export function DashboardLayout({ // ComponentType doesn't seem to work right, ReactNode is also incorrect // eslint-disable-next-line @typescript-eslint/no-explicit-any const CType = componentType as any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const PanelWrapperType = panelWrapper as any; // Props supplied by GoldenLayout // eslint-disable-next-line react/prop-types @@ -124,7 +132,10 @@ export function DashboardLayout({ return ( {/* eslint-disable-next-line react/jsx-props-no-spreading */} - + + {/* eslint-disable-next-line react/jsx-props-no-spreading */} + + ); } @@ -135,7 +146,7 @@ export function DashboardLayout({ dehydrateMap.set(name, componentDehydrate); return cleanup; }, - [hydrate, dehydrate, hydrateMap, dehydrateMap, layout] + [hydrate, dehydrate, hydrateMap, dehydrateMap, layout, panelWrapper] ); const hydrateComponent = useCallback( (name, props) => (hydrateMap.get(name) ?? FALLBACK_CALLBACK)(props, id),