mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Sidecar: Move service to runtime (#94860)
This commit is contained in:
@@ -44,6 +44,7 @@
|
|||||||
"@grafana/ui": "11.4.0-pre",
|
"@grafana/ui": "11.4.0-pre",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
|
"react-use": "17.5.1",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"tslib": "2.7.0"
|
"tslib": "2.7.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
import { useObservable } from 'react-use';
|
||||||
|
|
||||||
|
import { SidecarService_EXPERIMENTAL, sidecarServiceSingleton_EXPERIMENTAL } from './SidecarService_EXPERIMENTAL';
|
||||||
|
|
||||||
|
export const SidecarContext_EXPERIMENTAL = createContext<SidecarService_EXPERIMENTAL>(
|
||||||
|
sidecarServiceSingleton_EXPERIMENTAL
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main way to interact with the sidecar service inside a react context. It provides a wrapper around the
|
||||||
|
* service props so that even though they are observables we just pass actual values to the components.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export function useSidecar_EXPERIMENTAL() {
|
||||||
|
// As the sidecar service functionality is behind feature flag this does not need to be for now
|
||||||
|
const service = useContext(SidecarContext_EXPERIMENTAL);
|
||||||
|
|
||||||
|
if (!service) {
|
||||||
|
throw new Error('No SidecarContext found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const activePluginId = useObservable(service.activePluginIdObservable, service.activePluginId);
|
||||||
|
const initialContext = useObservable(service.initialContextObservable, service.initialContext);
|
||||||
|
|
||||||
|
return {
|
||||||
|
activePluginId,
|
||||||
|
initialContext,
|
||||||
|
// TODO: currently this allows anybody to open any app, in the future we should probably scope this to the
|
||||||
|
// current app but that means we will need to incorporate this better into the plugin platform APIs which
|
||||||
|
// we will do once the functionality is reasonably stable
|
||||||
|
openApp: (pluginId: string) => service.openApp(pluginId),
|
||||||
|
closeApp: (pluginId: string) => service.closeApp(pluginId),
|
||||||
|
isAppOpened: (pluginId: string) => service.isAppOpened(pluginId),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
import { config } from '../config';
|
||||||
|
|
||||||
|
import { locationService } from './LocationService';
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
localStorageKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a service that handles state and operation of a sidecar feature (sideview to render a second app in grafana).
|
||||||
|
* At this moment this is highly experimental and if used should be understand to break easily with newer versions.
|
||||||
|
* None of this functionality works without a feature toggle `appSidecar` being enabled.
|
||||||
|
*
|
||||||
|
* Right now this being in a single service is more of a practical tradeoff for easier isolation in the future these
|
||||||
|
* APIs may be integrated into other services or features like app extensions, plugin system etc.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
export class SidecarService_EXPERIMENTAL {
|
||||||
|
private _activePluginId: BehaviorSubject<string | undefined>;
|
||||||
|
private _initialContext: BehaviorSubject<unknown | undefined>;
|
||||||
|
private localStorageKey: string | undefined;
|
||||||
|
|
||||||
|
constructor(options: Options) {
|
||||||
|
this.localStorageKey = options.localStorageKey;
|
||||||
|
let initialId = undefined;
|
||||||
|
if (this.localStorageKey) {
|
||||||
|
initialId = localStorage.getItem(this.localStorageKey) || undefined;
|
||||||
|
}
|
||||||
|
this._activePluginId = new BehaviorSubject<string | undefined>(initialId);
|
||||||
|
this._initialContext = new BehaviorSubject<unknown | undefined>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
private assertFeatureEnabled() {
|
||||||
|
if (!config.featureToggles.appSidecar) {
|
||||||
|
console.warn('The `appSidecar` feature toggle is not enabled, doing nothing.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current app id of the app in sidecar. This is most probably provisional. In the future
|
||||||
|
* this should be driven by URL addressing so that routing for the apps don't change. Useful just internally
|
||||||
|
* to decide which app to render.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
get activePluginIdObservable() {
|
||||||
|
return this._activePluginId.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get initial context which is whatever data was passed when calling the 'openApp' function. This is meant as
|
||||||
|
* a way for the app to initialize it's state based on some context that is passed to it from the primary app.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
get initialContextObservable() {
|
||||||
|
return this._initialContext.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current value of the subject, this is needed if we want the value immediately. For example if used in
|
||||||
|
// hook in react with useObservable first render would return undefined even if the behaviourSubject has some
|
||||||
|
// value which will be emitted in the next tick and thus next rerender.
|
||||||
|
get initialContext() {
|
||||||
|
return this._initialContext.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
get activePluginId() {
|
||||||
|
return this._activePluginId.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an app in a sidecar. You can also pass some context object that will be then available to the app.
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
openApp(pluginId: string, context?: unknown) {
|
||||||
|
if (!this.assertFeatureEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.localStorageKey) {
|
||||||
|
localStorage.setItem(this.localStorageKey, pluginId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._activePluginId.next(pluginId);
|
||||||
|
this._initialContext.next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
closeApp(pluginId: string) {
|
||||||
|
if (!this.assertFeatureEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._activePluginId.getValue() === pluginId) {
|
||||||
|
if (this.localStorageKey) {
|
||||||
|
localStorage.removeItem(this.localStorageKey);
|
||||||
|
}
|
||||||
|
this._activePluginId.next(undefined);
|
||||||
|
this._initialContext.next(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is mainly useful inside an app extensions which are executed outside of the main app context but can work
|
||||||
|
* differently depending whether their app is currently rendered or not.
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
isAppOpened(pluginId: string) {
|
||||||
|
if (!this.assertFeatureEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._activePluginId.getValue() === pluginId || getMainAppPluginId() === pluginId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sidecarServiceSingleton_EXPERIMENTAL = new SidecarService_EXPERIMENTAL({
|
||||||
|
localStorageKey: 'grafana.sidecar.activePluginId',
|
||||||
|
});
|
||||||
|
|
||||||
|
// The app plugin that is "open" in the main Grafana view
|
||||||
|
function getMainAppPluginId() {
|
||||||
|
const { pathname } = locationService.getLocation();
|
||||||
|
|
||||||
|
// A naive way to sort of simulate core features being an app and having an appID
|
||||||
|
let mainApp = pathname.match(/\/a\/([^/]+)/)?.[1];
|
||||||
|
if (!mainApp && pathname.match(/\/explore/)) {
|
||||||
|
mainApp = 'explore';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mainApp && pathname.match(/\/d\//)) {
|
||||||
|
mainApp = 'dashboards';
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainApp || 'unknown';
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ export * from './legacyAngularInjector';
|
|||||||
export * from './live';
|
export * from './live';
|
||||||
export * from './LocationService';
|
export * from './LocationService';
|
||||||
export * from './appEvents';
|
export * from './appEvents';
|
||||||
|
export * from './SidecarService_EXPERIMENTAL';
|
||||||
|
export * from './SidecarContext_EXPERIMENTAL';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
setPluginExtensionGetter,
|
setPluginExtensionGetter,
|
||||||
|
|||||||
@@ -3,7 +3,13 @@ import { Component, ComponentType } from 'react';
|
|||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { Route, Routes } from 'react-router-dom-v5-compat';
|
import { Route, Routes } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
import { config, navigationLogger, reportInteraction } from '@grafana/runtime';
|
import {
|
||||||
|
config,
|
||||||
|
navigationLogger,
|
||||||
|
reportInteraction,
|
||||||
|
SidecarContext_EXPERIMENTAL,
|
||||||
|
sidecarServiceSingleton_EXPERIMENTAL,
|
||||||
|
} from '@grafana/runtime';
|
||||||
import { ErrorBoundaryAlert, GlobalStyles, PortalContainer } from '@grafana/ui';
|
import { ErrorBoundaryAlert, GlobalStyles, PortalContainer } from '@grafana/ui';
|
||||||
import { getAppRoutes } from 'app/routes/routes';
|
import { getAppRoutes } from 'app/routes/routes';
|
||||||
import { store } from 'app/store/store';
|
import { store } from 'app/store/store';
|
||||||
@@ -11,10 +17,8 @@ import { store } from 'app/store/store';
|
|||||||
import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabled';
|
import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabled';
|
||||||
import { GrafanaApp } from './app';
|
import { GrafanaApp } from './app';
|
||||||
import { GrafanaContext } from './core/context/GrafanaContext';
|
import { GrafanaContext } from './core/context/GrafanaContext';
|
||||||
import { SidecarContext } from './core/context/SidecarContext';
|
|
||||||
import { GrafanaRouteWrapper } from './core/navigation/GrafanaRoute';
|
import { GrafanaRouteWrapper } from './core/navigation/GrafanaRoute';
|
||||||
import { RouteDescriptor } from './core/navigation/types';
|
import { RouteDescriptor } from './core/navigation/types';
|
||||||
import { sidecarService } from './core/services/SidecarService';
|
|
||||||
import { ThemeProvider } from './core/utils/ConfigProvider';
|
import { ThemeProvider } from './core/utils/ConfigProvider';
|
||||||
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
|
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
|
||||||
import { ExtensionRegistriesProvider } from './features/plugins/extensions/ExtensionRegistriesContext';
|
import { ExtensionRegistriesProvider } from './features/plugins/extensions/ExtensionRegistriesContext';
|
||||||
@@ -96,7 +100,7 @@ export class AppWrapper extends Component<AppWrapperProps, AppWrapperState> {
|
|||||||
options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }}
|
options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }}
|
||||||
>
|
>
|
||||||
<GlobalStyles />
|
<GlobalStyles />
|
||||||
<SidecarContext.Provider value={sidecarService}>
|
<SidecarContext_EXPERIMENTAL.Provider value={sidecarServiceSingleton_EXPERIMENTAL}>
|
||||||
<ExtensionRegistriesProvider registries={app.pluginExtensionsRegistries}>
|
<ExtensionRegistriesProvider registries={app.pluginExtensionsRegistries}>
|
||||||
<div className="grafana-app">
|
<div className="grafana-app">
|
||||||
{config.featureToggles.appSidecar ? (
|
{config.featureToggles.appSidecar ? (
|
||||||
@@ -108,7 +112,7 @@ export class AppWrapper extends Component<AppWrapperProps, AppWrapperState> {
|
|||||||
<PortalContainer />
|
<PortalContainer />
|
||||||
</div>
|
</div>
|
||||||
</ExtensionRegistriesProvider>
|
</ExtensionRegistriesProvider>
|
||||||
</SidecarContext.Provider>
|
</SidecarContext_EXPERIMENTAL.Provider>
|
||||||
</KBarProvider>
|
</KBarProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</GrafanaContext.Provider>
|
</GrafanaContext.Provider>
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import { createContext, useContext } from 'react';
|
|
||||||
import { useObservable } from 'react-use';
|
|
||||||
|
|
||||||
import { SidecarService, sidecarService } from '../services/SidecarService';
|
|
||||||
|
|
||||||
export const SidecarContext = createContext<SidecarService>(sidecarService);
|
|
||||||
|
|
||||||
export function useSidecar() {
|
|
||||||
const activePluginId = useObservable(sidecarService.activePluginId);
|
|
||||||
const context = useContext(SidecarContext);
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
throw new Error('No SidecarContext found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
activePluginId,
|
|
||||||
openApp: (pluginId: string) => context.openApp(pluginId),
|
|
||||||
closeApp: (pluginId: string) => context.closeApp(pluginId),
|
|
||||||
isAppOpened: (pluginId: string) => context.isAppOpened(pluginId),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
|
||||||
|
|
||||||
import { config, locationService } from '@grafana/runtime';
|
|
||||||
|
|
||||||
interface Options {
|
|
||||||
localStorageKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SidecarService {
|
|
||||||
// The ID of the app plugin that is currently opened in the sidecar view
|
|
||||||
private _activePluginId: BehaviorSubject<string | undefined>;
|
|
||||||
private localStorageKey: string | undefined;
|
|
||||||
|
|
||||||
constructor(options: Options) {
|
|
||||||
this.localStorageKey = options.localStorageKey;
|
|
||||||
let initialId = undefined;
|
|
||||||
if (this.localStorageKey) {
|
|
||||||
initialId = localStorage.getItem(this.localStorageKey) || undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._activePluginId = new BehaviorSubject<string | undefined>(initialId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private assertFeatureEnabled() {
|
|
||||||
if (!config.featureToggles.appSidecar) {
|
|
||||||
console.warn('The `appSidecar` feature toggle is not enabled, doing nothing.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get activePluginId() {
|
|
||||||
return this._activePluginId.asObservable();
|
|
||||||
}
|
|
||||||
|
|
||||||
openApp(pluginId: string) {
|
|
||||||
if (!this.assertFeatureEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.localStorageKey) {
|
|
||||||
localStorage.setItem(this.localStorageKey, pluginId);
|
|
||||||
}
|
|
||||||
return this._activePluginId.next(pluginId);
|
|
||||||
}
|
|
||||||
|
|
||||||
closeApp(pluginId: string) {
|
|
||||||
if (!this.assertFeatureEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._activePluginId.getValue() === pluginId) {
|
|
||||||
if (this.localStorageKey) {
|
|
||||||
localStorage.removeItem(this.localStorageKey);
|
|
||||||
}
|
|
||||||
return this._activePluginId.next(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isAppOpened(pluginId: string) {
|
|
||||||
if (!this.assertFeatureEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._activePluginId.getValue() === pluginId || getMainAppPluginId() === pluginId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sidecarService = new SidecarService({ localStorageKey: 'grafana.sidecar.activePluginId' });
|
|
||||||
|
|
||||||
// The app plugin that is "open" in the main Grafana view
|
|
||||||
function getMainAppPluginId() {
|
|
||||||
const { pathname } = locationService.getLocation();
|
|
||||||
|
|
||||||
// A naive way to sort of simulate core features being an app and having an appID
|
|
||||||
let mainApp = pathname.match(/\/a\/([^/]+)/)?.[1];
|
|
||||||
if (!mainApp && pathname.match(/\/explore/)) {
|
|
||||||
mainApp = 'explore';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mainApp && pathname.match(/\/d\//)) {
|
|
||||||
mainApp = 'dashboards';
|
|
||||||
}
|
|
||||||
|
|
||||||
return mainApp || 'unknown';
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { useMemo } from 'react';
|
|||||||
import { useObservable } from 'react-use';
|
import { useObservable } from 'react-use';
|
||||||
|
|
||||||
import { PluginExtension, usePluginContext } from '@grafana/data';
|
import { PluginExtension, usePluginContext } from '@grafana/data';
|
||||||
import { GetPluginExtensionsOptions, UsePluginExtensionsResult } from '@grafana/runtime';
|
import { GetPluginExtensionsOptions, UsePluginExtensionsResult, useSidecar_EXPERIMENTAL } from '@grafana/runtime';
|
||||||
import { useSidecar } from 'app/core/context/SidecarContext';
|
|
||||||
|
|
||||||
import { getPluginExtensions } from './getPluginExtensions';
|
import { getPluginExtensions } from './getPluginExtensions';
|
||||||
import { log } from './logs/log';
|
import { log } from './logs/log';
|
||||||
@@ -19,7 +18,7 @@ export function createUsePluginExtensions(registries: PluginExtensionRegistries)
|
|||||||
const pluginContext = usePluginContext();
|
const pluginContext = usePluginContext();
|
||||||
const addedComponentsRegistry = useObservable(observableAddedComponentsRegistry);
|
const addedComponentsRegistry = useObservable(observableAddedComponentsRegistry);
|
||||||
const addedLinksRegistry = useObservable(observableAddedLinksRegistry);
|
const addedLinksRegistry = useObservable(observableAddedLinksRegistry);
|
||||||
const { activePluginId } = useSidecar();
|
const { activePluginId } = useSidecar_EXPERIMENTAL();
|
||||||
const { extensionPointId, context, limitPerPlugin } = options;
|
const { extensionPointId, context, limitPerPlugin } = options;
|
||||||
|
|
||||||
const { extensions } = useMemo(() => {
|
const { extensions } = useMemo(() => {
|
||||||
|
|||||||
@@ -20,11 +20,14 @@ import {
|
|||||||
PluginExtensionExposedComponentConfig,
|
PluginExtensionExposedComponentConfig,
|
||||||
PluginExtensionAddedComponentConfig,
|
PluginExtensionAddedComponentConfig,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { reportInteraction, config } from '@grafana/runtime';
|
import {
|
||||||
|
reportInteraction,
|
||||||
|
config,
|
||||||
|
// TODO: instead of depending on the service as a singleton, inject it as an argument from the React context
|
||||||
|
sidecarServiceSingleton_EXPERIMENTAL,
|
||||||
|
} from '@grafana/runtime';
|
||||||
import { Modal } from '@grafana/ui';
|
import { Modal } from '@grafana/ui';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
// TODO: instead of depending on the service as a singleton, inject it as an argument from the React context
|
|
||||||
import { sidecarService } from 'app/core/services/SidecarService';
|
|
||||||
import { getPluginSettings } from 'app/features/plugins/pluginSettings';
|
import { getPluginSettings } from 'app/features/plugins/pluginSettings';
|
||||||
import { ShowModalReactEvent } from 'app/types/events';
|
import { ShowModalReactEvent } from 'app/types/events';
|
||||||
|
|
||||||
@@ -388,7 +391,7 @@ export function getLinkExtensionOnClick(
|
|||||||
context,
|
context,
|
||||||
openModal: createOpenModalFunction(pluginId),
|
openModal: createOpenModalFunction(pluginId),
|
||||||
isAppOpened: () => isAppOpened(pluginId),
|
isAppOpened: () => isAppOpened(pluginId),
|
||||||
openAppInSideview: () => openAppInSideview(pluginId),
|
openAppInSideview: (context?: unknown) => openAppInSideview(pluginId, context),
|
||||||
closeAppInSideview: () => closeAppInSideview(pluginId),
|
closeAppInSideview: () => closeAppInSideview(pluginId),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -426,11 +429,12 @@ export function getLinkExtensionPathWithTracking(pluginId: string, path: string,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const openAppInSideview = (pluginId: string) => sidecarService.openApp(pluginId);
|
export const openAppInSideview = (pluginId: string, context?: unknown) =>
|
||||||
|
sidecarServiceSingleton_EXPERIMENTAL.openApp(pluginId, context);
|
||||||
|
|
||||||
export const closeAppInSideview = (pluginId: string) => sidecarService.closeApp(pluginId);
|
export const closeAppInSideview = (pluginId: string) => sidecarServiceSingleton_EXPERIMENTAL.closeApp(pluginId);
|
||||||
|
|
||||||
export const isAppOpened = (pluginId: string) => sidecarService.isAppOpened(pluginId);
|
export const isAppOpened = (pluginId: string) => sidecarServiceSingleton_EXPERIMENTAL.isAppOpened(pluginId);
|
||||||
|
|
||||||
// Comes from the `app_mode` setting in the Grafana config (defaults to "development")
|
// Comes from the `app_mode` setting in the Grafana config (defaults to "development")
|
||||||
// Can be set with the `GF_DEFAULT_APP_MODE` environment variable
|
// Can be set with the `GF_DEFAULT_APP_MODE` environment variable
|
||||||
|
|||||||
@@ -4,14 +4,19 @@ import { Router } from 'react-router-dom';
|
|||||||
import { CompatRouter } from 'react-router-dom-v5-compat';
|
import { CompatRouter } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data/';
|
import { GrafanaTheme2 } from '@grafana/data/';
|
||||||
import { HistoryWrapper, locationService, LocationServiceProvider, useChromeHeaderHeight } from '@grafana/runtime';
|
import {
|
||||||
|
HistoryWrapper,
|
||||||
|
locationService,
|
||||||
|
LocationServiceProvider,
|
||||||
|
useChromeHeaderHeight,
|
||||||
|
useSidecar_EXPERIMENTAL,
|
||||||
|
} from '@grafana/runtime';
|
||||||
import { GlobalStyles, IconButton, ModalRoot, Stack, useSplitter, useStyles2 } from '@grafana/ui';
|
import { GlobalStyles, IconButton, ModalRoot, Stack, useSplitter, useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { AngularRoot } from '../angular/AngularRoot';
|
import { AngularRoot } from '../angular/AngularRoot';
|
||||||
import { AppChrome } from '../core/components/AppChrome/AppChrome';
|
import { AppChrome } from '../core/components/AppChrome/AppChrome';
|
||||||
import { AppNotificationList } from '../core/components/AppNotifications/AppNotificationList';
|
import { AppNotificationList } from '../core/components/AppNotifications/AppNotificationList';
|
||||||
import { ModalsContextProvider } from '../core/context/ModalsContextProvider';
|
import { ModalsContextProvider } from '../core/context/ModalsContextProvider';
|
||||||
import { useSidecar } from '../core/context/SidecarContext';
|
|
||||||
import { QueriesDrawerContextProvider } from '../features/explore/QueriesDrawer/QueriesDrawerContext';
|
import { QueriesDrawerContextProvider } from '../features/explore/QueriesDrawer/QueriesDrawerContext';
|
||||||
import AppRootPage from '../features/plugins/components/AppRootPage';
|
import AppRootPage from '../features/plugins/components/AppRootPage';
|
||||||
|
|
||||||
@@ -58,7 +63,7 @@ export function RouterWrapper(props: RouterWrapperProps) {
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export function ExperimentalSplitPaneRouterWrapper(props: RouterWrapperProps) {
|
export function ExperimentalSplitPaneRouterWrapper(props: RouterWrapperProps) {
|
||||||
const { activePluginId, closeApp } = useSidecar();
|
const { activePluginId, closeApp } = useSidecar_EXPERIMENTAL();
|
||||||
|
|
||||||
let { containerProps, primaryProps, secondaryProps, splitterProps } = useSplitter({
|
let { containerProps, primaryProps, secondaryProps, splitterProps } = useSplitter({
|
||||||
direction: 'row',
|
direction: 'row',
|
||||||
|
|||||||
@@ -4038,6 +4038,7 @@ __metadata:
|
|||||||
lodash: "npm:4.17.21"
|
lodash: "npm:4.17.21"
|
||||||
react: "npm:18.2.0"
|
react: "npm:18.2.0"
|
||||||
react-dom: "npm:18.2.0"
|
react-dom: "npm:18.2.0"
|
||||||
|
react-use: "npm:17.5.1"
|
||||||
rimraf: "npm:6.0.1"
|
rimraf: "npm:6.0.1"
|
||||||
rollup: "npm:^4.22.4"
|
rollup: "npm:^4.22.4"
|
||||||
rollup-plugin-dts: "npm:^6.1.1"
|
rollup-plugin-dts: "npm:^6.1.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user