Plugins: Adds AppChrome extension point (#106623)

* Plugins: adds basic PopupExtension point

* Simplify the extension point.

* Excluding the app chrome extension to be rendered on login/signup pages.

* Added feature toggle to be able to disable the app chrome extensions.

* Adding an error boundary around the extension point.

* Changed the way we use the feature toggle.

* moved to use the helper function instead of filtering.

* removed duplace info.

* chore: remove leftover useMemo

---------

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
This commit is contained in:
Hugo Häggmark
2025-06-30 06:32:08 +02:00
committed by GitHub
co-authored by Marcus Andersson
parent 470f3c1578
commit 15293a2ceb
8 changed files with 87 additions and 0 deletions
@@ -1031,4 +1031,9 @@ export interface FeatureToggles {
* @default false
*/
newInfluxDSConfigPageDesign?: boolean;
/**
* Set this to true to enable all app chrome extensions registered by plugins.
* @default false
*/
enableAppChromeExtensions?: boolean;
}
@@ -194,6 +194,7 @@ export enum PluginExtensionPoints {
QueryEditorRowAdaptiveTelemetryV1 = 'grafana/query-editor-row/adaptivetelemetry/v1',
TraceViewResourceAttributes = 'grafana/traceview/resource-attributes',
LogsViewResourceAttributes = 'grafana/logsview/resource-attributes',
AppChrome = 'grafana/app/chrome/v1',
}
export type PluginExtensionPanelContext = {
+10
View File
@@ -1771,6 +1771,16 @@ var (
Owner: grafanaPartnerPluginsSquad,
Expression: "false",
},
{
Name: "enableAppChromeExtensions",
Description: "Set this to true to enable all app chrome extensions registered by plugins.",
Stage: FeatureStageExperimental,
Owner: grafanaPluginsPlatformSquad,
HideFromAdminPage: true,
HideFromDocs: true,
FrontendOnly: true,
Expression: "false", // extensions will be disabled by default
},
}
)
+1
View File
@@ -231,3 +231,4 @@ alertingImportAlertmanagerAPI,experimental,@grafana/alerting-squad,false,false,f
preferLibraryPanelTitle,privatePreview,@grafana/dashboards-squad,false,false,false
tabularNumbers,GA,@grafana/grafana-frontend-platform,false,false,false
newInfluxDSConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
enableAppChromeExtensions,experimental,@grafana/plugins-platform-backend,false,false,true
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
231 preferLibraryPanelTitle privatePreview @grafana/dashboards-squad false false false
232 tabularNumbers GA @grafana/grafana-frontend-platform false false false
233 newInfluxDSConfigPageDesign privatePreview @grafana/partner-datasources false false false
234 enableAppChromeExtensions experimental @grafana/plugins-platform-backend false false true
+4
View File
@@ -934,4 +934,8 @@ const (
// FlagNewInfluxDSConfigPageDesign
// Enables new design for the InfluxDB data source configuration page
FlagNewInfluxDSConfigPageDesign = "newInfluxDSConfigPageDesign"
// FlagEnableAppChromeExtensions
// Set this to true to enable all app chrome extensions registered by plugins.
FlagEnableAppChromeExtensions = "enableAppChromeExtensions"
)
+16
View File
@@ -943,6 +943,22 @@
"codeowner": "@grafana/aws-datasources"
}
},
{
"metadata": {
"name": "enableAppChromeExtensions",
"resourceVersion": "1750235111726",
"creationTimestamp": "2025-06-18T08:25:11Z"
},
"spec": {
"description": "Set this to true to enable all app chrome extensions registered by plugins.",
"stage": "experimental",
"codeowner": "@grafana/plugins-platform-backend",
"frontend": true,
"hideFromAdminPage": true,
"hideFromDocs": true,
"expression": "false"
}
},
{
"metadata": {
"name": "enableDatagridEditing",
@@ -0,0 +1,48 @@
import { useLocation } from 'react-router-dom';
import { PluginExtensionPoints } from '@grafana/data';
import { config, renderLimitedComponents, usePluginComponents } from '@grafana/runtime';
import { ErrorBoundaryAlert } from '@grafana/ui';
const excludedRoutes: Record<string, boolean> = {
'/login': true,
'/signup': true,
'/verify': true,
'/user/password/send-reset-email': true,
'/user/password/reset': true,
'/sandbox/benchmarks': true,
};
export function AppChromeExtensionPoint(): JSX.Element | null {
const location = useLocation();
if (config.featureToggles.enableAppChromeExtensions !== true) {
return null;
}
if (excludedRoutes[location.pathname]) {
return null;
}
return (
<ErrorBoundaryAlert>
<InternalAppChromeExtensionPoint />
</ErrorBoundaryAlert>
);
}
function InternalAppChromeExtensionPoint(): JSX.Element | null {
const { components, isLoading } = usePluginComponents({
extensionPointId: PluginExtensionPoints.AppChrome,
});
if (isLoading) {
return null;
}
return renderLimitedComponents({
props: {},
components: components,
pluginId: 'grafana-setupguide-app',
});
}
+2
View File
@@ -6,6 +6,7 @@ import { locationService, LocationServiceProvider } from '@grafana/runtime';
import { ModalRoot, Stack } from '@grafana/ui';
import { AppChrome } from '../core/components/AppChrome/AppChrome';
import { AppChromeExtensionPoint } from '../core/components/AppChrome/AppChromeExtensionPoint';
import { AppNotificationList } from '../core/components/AppNotifications/AppNotificationList';
import { ModalsContextProvider } from '../core/context/ModalsContextProvider';
import { QueriesDrawerContextProvider } from '../features/explore/QueriesDrawer/QueriesDrawerContext';
@@ -41,6 +42,7 @@ export function RouterWrapper(props: RouterWrapperProps) {
{props.bodyRenderHooks.map((Hook, index) => (
<Hook key={index.toString()} />
))}
<AppChromeExtensionPoint />
</AppChrome>
<ModalRoot />
</ModalsContextProvider>