From 2f41c7c289423e1f7c20706447ecc82959a269fb Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Mon, 5 May 2025 14:25:02 -0700 Subject: [PATCH] Extension Sidebar: Fix not opening the stored component (#104649) * Extension Sidebar: Fix not opening the stored component * empty --- .../ExtensionSidebarProvider.tsx | 77 ++++++++++++------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx index f39c05766fc..e883668cd52 100644 --- a/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx +++ b/public/app/core/components/AppChrome/ExtensionSidebar/ExtensionSidebarProvider.tsx @@ -1,4 +1,4 @@ -import { createContext, ReactNode, useCallback, useContext, useEffect, useState } from 'react'; +import { createContext, ReactNode, useCallback, useContext, useEffect, useState, useMemo } from 'react'; import { useLocalStorage } from 'react-use'; import { store, type ExtensionInfo } from '@grafana/data'; @@ -92,7 +92,7 @@ export const ExtensionSidebarContextProvider = ({ children }: ExtensionSidebarCo // that means, a plugin would need to register both, a link and a component to // `grafana/extension-sidebar/v0-alpha` and the link's `configure` method would control // whether the component is rendered or not - const { links } = usePluginLinks({ + const { links, isLoading } = usePluginLinks({ extensionPointId: EXTENSION_SIDEBAR_EXTENSION_POINT_ID, context: { path: currentPath, @@ -102,42 +102,59 @@ export const ExtensionSidebarContextProvider = ({ children }: ExtensionSidebarCo const isEnabled = !!config.featureToggles.extensionSidebar; // get all components for this extension point, but only for the permitted plugins // if the extension sidebar is not enabled, we will return an empty map - const availableComponents = isEnabled - ? new Map( - Array.from(getExtensionPointPluginMeta(EXTENSION_SIDEBAR_EXTENSION_POINT_ID).entries()).filter( - ([pluginId, pluginMeta]) => - PERMITTED_EXTENSION_SIDEBAR_PLUGINS.includes(pluginId) && - links.some( - (link) => - link.pluginId === pluginId && - pluginMeta.addedComponents.some((component) => component.title === link.title) + const availableComponents = useMemo( + () => + isEnabled + ? new Map( + Array.from(getExtensionPointPluginMeta(EXTENSION_SIDEBAR_EXTENSION_POINT_ID).entries()).filter( + ([pluginId, pluginMeta]) => + PERMITTED_EXTENSION_SIDEBAR_PLUGINS.includes(pluginId) && + links.some( + (link) => + link.pluginId === pluginId && + pluginMeta.addedComponents.some((component) => component.title === link.title) + ) ) - ) - ) - : new Map< - string, - { - readonly addedComponents: ExtensionInfo[]; - readonly addedLinks: ExtensionInfo[]; - } - >(); + ) + : new Map< + string, + { + readonly addedComponents: ExtensionInfo[]; + readonly addedLinks: ExtensionInfo[]; + } + >(), + [isEnabled, links] + ); // check if the stored docked component is still available let defaultDockedComponentId: string | undefined; if (storedDockedPluginId) { const dockedMeta = getComponentMetaFromComponentId(storedDockedPluginId); if (dockedMeta) { - const plugin = availableComponents.get(dockedMeta.pluginId); - if (plugin) { - const component = plugin.addedComponents.find((c) => c.title === dockedMeta.componentTitle); - if (component) { - defaultDockedComponentId = storedDockedPluginId; - } - } + defaultDockedComponentId = storedDockedPluginId; } } const [dockedComponentId, setDockedComponentId] = useState(defaultDockedComponentId); + useEffect(() => { + if (isLoading) { + return; + } + + if (dockedComponentId) { + const dockedMeta = getComponentMetaFromComponentId(dockedComponentId); + if (dockedMeta) { + const plugin = availableComponents.get(dockedMeta.pluginId); + if (!plugin || !plugin.addedComponents.some((c) => c.title === dockedMeta.componentTitle)) { + setDockedComponentId(undefined); + } + } else { + // no component found, so we clear the docked component id + setDockedComponentId(undefined); + } + } + }, [isLoading, availableComponents, dockedComponentId]); + const setDockedComponentWithProps = useCallback( (componentId: string | undefined, props?: Record) => { setProps(props); @@ -173,6 +190,10 @@ export const ExtensionSidebarContextProvider = ({ children }: ExtensionSidebarCo // update the stored docked component id when it changes useEffect(() => { + // wait for the plugin links to be loaded before we update the stored docked component id + if (isLoading) { + return; + } const componentMeta = getComponentMetaFromComponentId(dockedComponentId ?? ''); const storedComponentId = store.get(EXTENSION_SIDEBAR_DOCKED_LOCAL_STORAGE_KEY); const storedComponentMeta = getComponentMetaFromComponentId(storedComponentId ?? ''); @@ -191,7 +212,7 @@ export const ExtensionSidebarContextProvider = ({ children }: ExtensionSidebarCo } else { store.delete(EXTENSION_SIDEBAR_DOCKED_LOCAL_STORAGE_KEY); } - }, [dockedComponentId]); + }, [dockedComponentId, isLoading]); return (