From e7c30a8a05363948380607d4258907b4ebe458c4 Mon Sep 17 00:00:00 2001
From: Gurinder Singh <gurinder@coderabbit.ai>
Date: Fri, 29 Nov 2024 12:51:59 -0500
Subject: [PATCH 1/5] custom dashboard checkpoint

---
 .../src/services/LocationService.tsx          |   1 +
 public/app/core/components/Page/Page.tsx      |  26 +--
 public/app/core/reducers/fn-slice.ts          |   2 +
 .../AddPanelButton/AddPanelButton.tsx         |   7 +-
 .../dashboard/containers/DashboardPage.tsx    |  95 +++++---
 .../dashboard/dashgrid/DashboardGrid.tsx      | 202 +++++++++++++-----
 .../dashboard/dashgrid/DashboardPanel.tsx     |   2 +-
 .../dashboard/dashgrid/PanelStateWrapper.tsx  |   2 +-
 public/app/fn-app/create-mfe.ts               |   1 +
 public/app/fn-app/fn-app-provider.tsx         |  39 +++-
 .../fn-dashboard-page/render-fn-dashboard.tsx |   2 +-
 public/app/fn-app/types.ts                    |   1 +
 12 files changed, 268 insertions(+), 112 deletions(-)

diff --git a/packages/grafana-runtime/src/services/LocationService.tsx b/packages/grafana-runtime/src/services/LocationService.tsx
index 5b0bcd9e0ccf8..c51b6894705e7 100644
--- a/packages/grafana-runtime/src/services/LocationService.tsx
+++ b/packages/grafana-runtime/src/services/LocationService.tsx
@@ -21,6 +21,7 @@ export interface LocationService {
   getHistory: () => H.History;
   getSearch: () => URLSearchParams;
   getSearchObject: () => UrlQueryMap;
+  fnPathnameChange: (path: string, queryParams: any) => void;
 
   /**
    * This is from the old LocationSrv interface
diff --git a/public/app/core/components/Page/Page.tsx b/public/app/core/components/Page/Page.tsx
index 0f82fd4ba8ced..cd29bbfb55619 100644
--- a/public/app/core/components/Page/Page.tsx
+++ b/public/app/core/components/Page/Page.tsx
@@ -2,7 +2,6 @@ import { css, cx } from '@emotion/css';
 import { useLayoutEffect } from 'react';
 
 import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
-import { config } from '@grafana/runtime';
 import { useStyles2 } from '@grafana/ui';
 import { useGrafana } from 'app/core/context/GrafanaContext';
 
@@ -94,24 +93,13 @@ Page.Contents = PageContents;
 
 const getStyles = (theme: GrafanaTheme2) => {
   return {
-    wrapper: css(
-      config.featureToggles.bodyScrolling
-        ? {
-            label: 'page-wrapper',
-            display: 'flex',
-            flex: '1 1 0',
-            flexDirection: 'column',
-            position: 'relative',
-          }
-        : {
-            label: 'page-wrapper',
-            height: '100%',
-            display: 'flex',
-            flex: '1 1 0',
-            flexDirection: 'column',
-            minHeight: 0,
-          }
-    ),
+    wrapper: css({
+      label: 'page-wrapper',
+      display: 'flex',
+      flex: '1 1 0',
+      flexDirection: 'column',
+      position: 'relative',
+    }),
     pageContent: css({
       label: 'page-content',
       flexGrow: 1,
diff --git a/public/app/core/reducers/fn-slice.ts b/public/app/core/reducers/fn-slice.ts
index d71ce9aef766e..732ca579c5a6a 100644
--- a/public/app/core/reducers/fn-slice.ts
+++ b/public/app/core/reducers/fn-slice.ts
@@ -6,6 +6,7 @@ import { AnyObject } from '../../fn-app/types';
 
 export interface FnGlobalState {
   FNDashboard: boolean;
+  isCustomDashboard: boolean;
   uid: string;
   slug: string;
   version: number;
@@ -48,6 +49,7 @@ export const FN_STATE_KEY = 'fnGlobalState';
 export const INITIAL_FN_STATE: FnGlobalState = {
   // NOTE: initial value is false
   FNDashboard: false,
+  isCustomDashboard: false,
   uid: '',
   slug: '',
   version: 1,
diff --git a/public/app/features/dashboard/components/AddPanelButton/AddPanelButton.tsx b/public/app/features/dashboard/components/AddPanelButton/AddPanelButton.tsx
index e76b3769ad076..8d7643ef6f4b4 100644
--- a/public/app/features/dashboard/components/AddPanelButton/AddPanelButton.tsx
+++ b/public/app/features/dashboard/components/AddPanelButton/AddPanelButton.tsx
@@ -10,9 +10,10 @@ import AddPanelMenu from './AddPanelMenu';
 export interface Props {
   dashboard: DashboardModel;
   onToolbarAddMenuOpen?: () => void;
+  isFNDashboard?: boolean;
 }
 
-const AddPanelButton = ({ dashboard, onToolbarAddMenuOpen }: Props) => {
+const AddPanelButton = ({ dashboard, onToolbarAddMenuOpen, isFNDashboard }: Props) => {
   const [isMenuOpen, setIsMenuOpen] = useState(false);
 
   useEffect(() => {
@@ -29,8 +30,8 @@ const AddPanelButton = ({ dashboard, onToolbarAddMenuOpen }: Props) => {
       onVisibleChange={setIsMenuOpen}
     >
       <Button
-        variant="secondary"
-        size="sm"
+        variant="primary"
+        size={isFNDashboard ? 'md' : 'sm'}
         fill="outline"
         data-testid={selectors.components.PageToolbar.itemButton('Add button')}
       >
diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx
index 5ebe4cbcd006c..7c869e13be3eb 100644
--- a/public/app/features/dashboard/containers/DashboardPage.tsx
+++ b/public/app/features/dashboard/containers/DashboardPage.tsx
@@ -1,22 +1,25 @@
 import { cx } from '@emotion/css';
 import { Portal } from '@mui/material';
-import React, { PureComponent } from 'react';
+import { PureComponent } from 'react';
 import { connect, ConnectedProps, MapDispatchToProps, MapStateToProps } from 'react-redux';
 
 import { NavModel, NavModelItem, TimeRange, PageLayoutType, locationUtil } from '@grafana/data';
 import { selectors } from '@grafana/e2e-selectors';
 import { config, locationService } from '@grafana/runtime';
-import { Themeable2, withTheme2, ToolbarButtonRow } from '@grafana/ui';
+import { Themeable2, withTheme2, ToolbarButtonRow, ToolbarButton, ModalsController } from '@grafana/ui';
 import { notifyApp } from 'app/core/actions';
+import { ScrollRefElement } from 'app/core/components/NativeScrollbar';
 import { Page } from 'app/core/components/Page/Page';
 import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound';
 import { GrafanaContext, GrafanaContextType } from 'app/core/context/GrafanaContext';
 import { createErrorNotification } from 'app/core/copy/appNotification';
+import { t } from 'app/core/internationalization';
 import { getKioskMode } from 'app/core/navigation/kiosk';
 import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
 import { FnGlobalState } from 'app/core/reducers/fn-slice';
 import { getNavModel } from 'app/core/selectors/navModel';
 import { PanelModel } from 'app/features/dashboard/state';
+import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
 import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
 import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
 import { getPageNavFromSlug, getRootContentNavModel } from 'app/features/storage/StorageFolderPage';
@@ -26,6 +29,7 @@ import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events';
 
 import { cancelVariables, templateVarsChangedInUrl } from '../../variables/state/actions';
 import { findTemplateVarChanges } from '../../variables/utils';
+import AddPanelButton from '../components/AddPanelButton/AddPanelButton';
 import { AddWidgetModal } from '../components/AddWidgetModal/AddWidgetModal';
 import { DashNav } from '../components/DashNav';
 import { DashNavTimeControls } from '../components/DashNav/DashNavTimeControls';
@@ -36,6 +40,7 @@ import { DashboardPrompt } from '../components/DashboardPrompt/DashboardPrompt';
 import { DashboardSettings } from '../components/DashboardSettings';
 import { PanelInspector } from '../components/Inspector/PanelInspector';
 import { PanelEditor } from '../components/PanelEditor/PanelEditor';
+import { SaveDashboardDrawer } from '../components/SaveDashboard/SaveDashboardDrawer';
 import { SubMenu } from '../components/SubMenu/SubMenu';
 import { DashboardGrid } from '../dashgrid/DashboardGrid';
 import { liveTimer } from '../dashgrid/liveTimer';
@@ -72,7 +77,7 @@ export type MapStateToDashboardPageProps = MapStateToProps<
   Pick<DashboardState, 'initPhase' | 'initError'> & {
     dashboard: ReturnType<DashboardState['getModel']>;
     navIndex: StoreState['navIndex'];
-  } & Pick<FnGlobalState, 'FNDashboard' | 'controlsContainer'>,
+  } & Pick<FnGlobalState, 'FNDashboard' | 'controlsContainer' | 'isCustomDashboard'>,
   OwnProps,
   StoreState
 >;
@@ -93,6 +98,7 @@ export const mapStateToProps: MapStateToDashboardPageProps = (state) => ({
   dashboard: state.dashboard.getModel(),
   navIndex: state.navIndex,
   FNDashboard: state.fnGlobalState.FNDashboard,
+  isCustomDashboard: state.fnGlobalState.isCustomDashboard,
   controlsContainer: state.fnGlobalState.controlsContainer,
 });
 
@@ -128,7 +134,7 @@ export interface State {
   showLoadingState: boolean;
   panelNotFound: boolean;
   editPanelAccessDenied: boolean;
-  scrollElement?: HTMLDivElement;
+  scrollElement?: ScrollRefElement;
   pageNav?: NavModelItem;
   sectionNav?: NavModel;
 }
@@ -152,9 +158,9 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
 
   componentDidMount() {
     this.initDashboard();
-    const { FNDashboard } = this.props;
+    const { FNDashboard, isCustomDashboard } = this.props;
 
-    if (!FNDashboard) {
+    if (!FNDashboard || isCustomDashboard) {
       this.forceRouteReloadCounter = (this.props.history.location?.state as any)?.routeReloadCounter || 0;
     }
   }
@@ -192,13 +198,13 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
   }
 
   componentDidUpdate(prevProps: Props, prevState: State) {
-    const { dashboard, match, templateVarsChangedInUrl, FNDashboard } = this.props;
+    const { dashboard, match, templateVarsChangedInUrl, FNDashboard, isCustomDashboard } = this.props;
 
     if (!dashboard) {
       return;
     }
 
-    if (!FNDashboard) {
+    if (!FNDashboard || isCustomDashboard) {
       const routeReloadCounter = (this.props.history.location?.state as any)?.routeReloadCounter;
 
       if (
@@ -354,7 +360,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
     this.setState({ updateScrollTop: 0 });
   };
 
-  setScrollRef = (scrollElement: HTMLDivElement): void => {
+  setScrollRef = (scrollElement: ScrollRefElement): void => {
     this.setState({ scrollElement });
   };
 
@@ -378,7 +384,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
   }
 
   render() {
-    const { dashboard, initError, queryParams, FNDashboard, controlsContainer } = this.props;
+    const { dashboard, initError, queryParams, FNDashboard, controlsContainer, isCustomDashboard } = this.props;
     const { editPanel, viewPanel, pageNav, sectionNav } = this.state;
     const kioskMode = getKioskMode(this.props.queryParams);
 
@@ -391,12 +397,22 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
 
     const showToolbar = FNDashboard || (kioskMode !== KioskMode.Full && !queryParams.editview);
 
+    const isFNDashboardEditable = (isCustomDashboard && FNDashboard) || !FNDashboard;
+
+    console.log('Edit Panel: ', { editPanel, sectionNav, pageNav, isFNDashboardEditable });
+    console.log('Dashboard settings: ', { editView: queryParams.editview, pageNav, sectionNav, isFNDashboardEditable });
+    console.log('Add Widget: ', {
+      isFNDashboardEditable,
+      addWidget: queryParams.addWidget,
+      configToggle: config.featureToggles.vizAndWidgetSplit,
+    });
+
     const pageClassName = cx({
       'panel-in-fullscreen': Boolean(viewPanel),
       'page-hidden': Boolean(queryParams.editview || editPanel),
     });
 
-    if (dashboard.meta.dashboardNotFound) {
+    if (dashboard.meta.dashboardNotFound && !FNDashboard) {
       return (
         <Page navId="dashboards/browse" layout={PageLayoutType.Canvas} pageNav={{ text: 'Not found' }}>
           <EntityNotFound entity="Dashboard" />
@@ -417,27 +433,56 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
     );
 
     return (
-      <React.Fragment>
+      <>
         <Page
           navModel={sectionNav}
           pageNav={pageNav}
           layout={PageLayoutType.Canvas}
           className={pageClassName}
-          // scrollRef={this.setScrollRef}
-          // scrollTop={updateScrollTop}
-          style={{ minHeight: '550px' }}
+          onSetScrollRef={this.setScrollRef}
         >
           {showToolbar && (
             <header data-testid={selectors.pages.Dashboard.DashNav.navV2}>
               {FNDashboard ? (
-                FNTimeRange
+                <div
+                  style={{
+                    display: 'flex',
+                    justifyContent: 'flex-end',
+                    gap: 4,
+                  }}
+                >
+                  {isCustomDashboard && (
+                    <>
+                      <ModalsController key="button-save">
+                        {({ showModal, hideModal }) => (
+                          <ToolbarButton
+                            tooltip={t('dashboard.toolbar.save', 'Save dashboard')}
+                            icon="save"
+                            onClick={() => {
+                              showModal(SaveDashboardDrawer, {
+                                dashboard,
+                                onDismiss: hideModal,
+                              });
+                            }}
+                          />
+                        )}
+                      </ModalsController>
+                      <AddPanelButton
+                        onToolbarAddMenuOpen={DashboardInteractions.toolbarAddClick}
+                        dashboard={dashboard}
+                        key="panel-add-dropdown"
+                        isFNDashboard
+                      />
+                    </>
+                  )}
+                  {FNTimeRange}
+                </div>
               ) : (
                 <DashNav
                   dashboard={dashboard}
                   title={dashboard.title}
                   folderTitle={dashboard.meta.folderTitle}
                   isFullscreen={!!viewPanel}
-                  // onAddPanel={this.onAddPanel}
                   kioskMode={kioskMode}
                   hideTimePicker={dashboard.timepicker.hidden}
                 />
@@ -453,14 +498,14 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
           )}
           <DashboardGrid
             dashboard={dashboard}
-            isEditable={!!dashboard.meta.canEdit && !FNDashboard}
+            isEditable={isFNDashboardEditable && !!dashboard.meta.canEdit}
             viewPanel={viewPanel}
             editPanel={editPanel}
           />
 
           {inspectPanel && !FNDashboard && <PanelInspector dashboard={dashboard} panel={inspectPanel} />}
         </Page>
-        {editPanel && !FNDashboard && sectionNav && pageNav && (
+        {editPanel && sectionNav && pageNav && isFNDashboardEditable && (
           <PanelEditor
             dashboard={dashboard}
             sourcePanel={editPanel}
@@ -469,7 +514,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
             pageNav={pageNav}
           />
         )}
-        {queryParams.editview && !FNDashboard && pageNav && sectionNav && (
+        {queryParams.editview && pageNav && sectionNav && isFNDashboardEditable && (
           <DashboardSettings
             dashboard={dashboard}
             editview={queryParams.editview}
@@ -477,16 +522,18 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
             sectionNav={sectionNav}
           />
         )}
-        {!FNDashboard && queryParams.addWidget && config.featureToggles.vizAndWidgetSplit && <AddWidgetModal />}
-      </React.Fragment>
+        {isFNDashboardEditable && queryParams.addWidget && config.featureToggles.vizAndWidgetSplit && (
+          <AddWidgetModal />
+        )}
+      </>
     );
   }
 }
 
 function updateStatePageNavFromProps(props: Props, state: State): State {
-  const { dashboard, FNDashboard } = props;
+  const { dashboard, FNDashboard, isCustomDashboard } = props;
 
-  if (!dashboard || FNDashboard) {
+  if (!dashboard || (FNDashboard && !isCustomDashboard)) {
     return state;
   }
 
diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
index 70c88a5d54182..5bf46d2e6c97f 100644
--- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
+++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx
@@ -1,13 +1,15 @@
 import classNames from 'classnames';
-import React, { PureComponent, CSSProperties } from 'react';
+import { PureComponent, CSSProperties } from 'react';
+import * as React from 'react';
 import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
 import { connect } from 'react-redux';
-import AutoSizer from 'react-virtualized-auto-sizer';
 import { Subscription } from 'rxjs';
 
 import { config } from '@grafana/runtime';
+import appEvents from 'app/core/app_events';
 import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core/constants';
 import { contextSrv } from 'app/core/services/context_srv';
+import { VariablesChanged } from 'app/features/variables/types';
 import { StoreState } from 'app/types';
 import { DashboardPanelsChangedEvent } from 'app/types/events';
 
@@ -19,6 +21,8 @@ import { GridPos } from '../state/PanelModel';
 import DashboardEmpty from './DashboardEmpty';
 import { DashboardPanel } from './DashboardPanel';
 
+export const PANEL_FILTER_VARIABLE = 'systemPanelFilterVar';
+
 export interface Props {
   dashboard: DashboardModel;
   isEditable: boolean;
@@ -26,9 +30,15 @@ export interface Props {
   viewPanel: PanelModel | null;
   hidePanelMenus?: boolean;
   isFnDashboard?: boolean;
+  isCustomDashboard?: boolean;
+}
+
+interface State {
+  panelFilter?: RegExp;
+  width: number;
 }
 
-export class Component extends PureComponent<Props> {
+export class DashboardGridUnconnected extends PureComponent<Props, State> {
   private panelMap: { [key: string]: PanelModel } = {};
   private eventSubs = new Subscription();
   private windowHeight = 1200;
@@ -40,10 +50,41 @@ export class Component extends PureComponent<Props> {
 
   constructor(props: Props) {
     super(props);
+    this.state = {
+      panelFilter: undefined,
+      width: document.body.clientWidth, // initial very rough estimate
+    };
   }
 
   componentDidMount() {
     const { dashboard } = this.props;
+
+    if (config.featureToggles.panelFilterVariable) {
+      // If panel filter variable is set on load then
+      // update state to filter panels
+      for (const variable of dashboard.getVariables()) {
+        if (variable.id === PANEL_FILTER_VARIABLE) {
+          if ('query' in variable) {
+            this.setPanelFilter(variable.query);
+          }
+          break;
+        }
+      }
+
+      this.eventSubs.add(
+        appEvents.subscribe(VariablesChanged, (e) => {
+          if (e.payload.variable?.id === PANEL_FILTER_VARIABLE) {
+            if ('current' in e.payload.variable) {
+              let variable = e.payload.variable.current;
+              if ('value' in variable && typeof variable.value === 'string') {
+                this.setPanelFilter(variable.value);
+              }
+            }
+          }
+        })
+      );
+    }
+
     this.eventSubs.add(dashboard.events.subscribe(DashboardPanelsChangedEvent, this.triggerForceUpdate));
   }
 
@@ -51,10 +92,25 @@ export class Component extends PureComponent<Props> {
     this.eventSubs.unsubscribe();
   }
 
+  setPanelFilter(regex: string) {
+    // Only set the panels filter if the systemPanelFilterVar variable
+    // is a non-empty string
+    let panelFilter = undefined;
+    if (regex.length > 0) {
+      panelFilter = new RegExp(regex, 'i');
+    }
+
+    this.setState({
+      panelFilter: panelFilter,
+    });
+  }
+
   buildLayout() {
     const layout: ReactGridLayout.Layout[] = [];
     this.panelMap = {};
+    const { panelFilter } = this.state;
 
+    let count = 0;
     for (const panel of this.props.dashboard.panels) {
       if (!panel.key) {
         panel.key = `panel-${panel.id}-${Date.now()}`;
@@ -81,13 +137,27 @@ export class Component extends PureComponent<Props> {
         panelPos.isDraggable = panel.collapsed;
       }
 
-      layout.push(panelPos);
+      if (!panelFilter) {
+        layout.push(panelPos);
+      } else {
+        if (panelFilter.test(panel.title)) {
+          panelPos.isResizable = false;
+          panelPos.isDraggable = false;
+          panelPos.x = (count % 2) * GRID_COLUMN_COUNT;
+          panelPos.y = Math.floor(count / 2);
+          layout.push(panelPos);
+          count++;
+        }
+      }
     }
 
     return layout;
   }
 
   onLayoutChange = (newLayout: ReactGridLayout.Layout[]) => {
+    if (this.state.panelFilter) {
+      return;
+    }
     for (const newPos of newLayout) {
       this.panelMap[newPos.i!].updateGridPos(newPos, this.isLayoutInitialized);
     }
@@ -139,6 +209,7 @@ export class Component extends PureComponent<Props> {
   }
 
   renderPanels(gridWidth: number, isDashboardDraggable: boolean) {
+    const { panelFilter } = this.state;
     const panelElements = [];
 
     // Reset last panel bottom
@@ -155,7 +226,7 @@ export class Component extends PureComponent<Props> {
     for (const panel of this.props.dashboard.panels) {
       const panelClasses = classNames({ 'react-grid-item--fullscreen': panel.isViewing });
 
-      panelElements.push(
+      const p = (
         <GrafanaGridItem
           key={panel.key}
           className={panelClasses}
@@ -171,6 +242,14 @@ export class Component extends PureComponent<Props> {
           }}
         </GrafanaGridItem>
       );
+
+      if (!panelFilter) {
+        panelElements.push(p);
+      } else {
+        if (panelFilter.test(panel.title)) {
+          panelElements.push(p);
+        }
+      }
     }
 
     return panelElements;
@@ -213,61 +292,69 @@ export class Component extends PureComponent<Props> {
     }
   };
 
+  private resizeObserver?: ResizeObserver;
+  private rootEl: HTMLDivElement | null = null;
+  onMeasureRef = (rootEl: HTMLDivElement | null) => {
+    if (!rootEl) {
+      if (this.rootEl && this.resizeObserver) {
+        this.resizeObserver.unobserve(this.rootEl);
+      }
+      return;
+    }
+
+    this.rootEl = rootEl;
+    this.resizeObserver = new ResizeObserver((entries) => {
+      entries.forEach((entry) => {
+        this.setState({ width: entry.contentRect.width });
+      });
+    });
+
+    this.resizeObserver.observe(rootEl);
+  };
+
   render() {
-    const { isEditable, dashboard, isFnDashboard } = this.props;
+    const { isEditable, dashboard, isCustomDashboard, isFnDashboard } = this.props;
+    const { width } = this.state;
 
-    if (config.featureToggles.emptyDashboardPage && dashboard.panels.length === 0) {
+    if (dashboard.panels.length === 0) {
       return <DashboardEmpty dashboard={dashboard} canCreate={isEditable} />;
     }
 
-    /**
-     * We have a parent with "flex: 1 1 0" we need to reset it to "flex: 1 1 auto" to have the AutoSizer
-     * properly working. For more information go here:
-     * https://github.com/bvaughn/react-virtualized/blob/master/docs/usingAutoSizer.md#can-i-use-autosizer-within-a-flex-container
-     */
-    return (
-      <div style={{ flex: '1 1 auto', display: this.props.editPanel ? 'none' : undefined }}>
-        <AutoSizer disableHeight>
-          {({ width }) => {
-            if (width === 0) {
-              return null;
-            }
+    const draggable = width <= config.theme2.breakpoints.values.md ? false : isEditable;
 
-            // Disable draggable if mobile device, solving an issue with unintentionally
-            // moving panels. https://github.com/grafana/grafana/issues/18497
-            const isLg = width <= config.theme2.breakpoints.values.md;
-            const draggable = isLg ? false : isEditable;
-
-            return (
-              /**
-               * The children is using a width of 100% so we need to guarantee that it is wrapped
-               * in an element that has the calculated size given by the AutoSizer. The AutoSizer
-               * has a width of 0 and will let its content overflow its div.
-               */
-              <div style={{ width: width, height: '100%' }} ref={this.onGetWrapperDivRef}>
-                <ReactGridLayout
-                  width={width}
-                  isDraggable={isFnDashboard ? false : draggable}
-                  isResizable={isFnDashboard ? false : isEditable}
-                  containerPadding={[0, 0]}
-                  useCSSTransforms={true}
-                  margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
-                  cols={GRID_COLUMN_COUNT}
-                  rowHeight={GRID_CELL_HEIGHT}
-                  draggableHandle=".grid-drag-handle"
-                  draggableCancel=".grid-drag-cancel"
-                  layout={this.buildLayout()}
-                  onDragStop={this.onDragStop}
-                  onResize={this.onResize}
-                  onResizeStop={this.onResizeStop}
-                  onLayoutChange={this.onLayoutChange}
-                >
-                  {this.renderPanels(width, draggable)}
-                </ReactGridLayout>
-              </div>
-            );
-          }}
-        </AutoSizer>
+    // pos: rel + z-index is required to create a new stacking context to contain
+    // the escalating z-indexes of the panels
+    return (
+      <div
+        ref={this.onMeasureRef}
+        style={{
+          flex: '1 1 auto',
+          position: 'relative',
+          zIndex: 1,
+          display: this.props.editPanel ? 'none' : undefined,
+        }}
+      >
+        <div style={{ width: width, height: '100%' }} ref={this.onGetWrapperDivRef}>
+          <ReactGridLayout
+            width={width}
+            isDraggable={isFnDashboard && !isCustomDashboard ? false : draggable}
+            isResizable={isFnDashboard && !isCustomDashboard ? false : isEditable}
+            containerPadding={[0, 0]}
+            useCSSTransforms={true}
+            margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
+            cols={GRID_COLUMN_COUNT}
+            rowHeight={GRID_CELL_HEIGHT}
+            draggableHandle=".grid-drag-handle"
+            draggableCancel=".grid-drag-cancel"
+            layout={this.buildLayout()}
+            onDragStop={this.onDragStop}
+            onResize={this.onResize}
+            onResizeStop={this.onResizeStop}
+            onLayoutChange={this.onLayoutChange}
+          >
+            {this.renderPanels(width, draggable)}
+          </ReactGridLayout>
+        </div>
       </div>
     );
   }
@@ -279,7 +366,7 @@ interface GrafanaGridItemProps extends React.HTMLAttributes<HTMLDivElement> {
   isViewing: boolean;
   windowHeight: number;
   windowWidth: number;
-  children: any;
+  children: any; // eslint-disable-line @typescript-eslint/no-explicit-any
 }
 
 /**
@@ -320,7 +407,7 @@ const GrafanaGridItem = React.forwardRef<HTMLDivElement, GrafanaGridItemProps>((
 
   // props.children[0] is our main children. RGL adds the drag handle at props.children[1]
   return (
-    <div {...divProps} ref={ref}>
+    <div {...divProps} style={{ ...divProps.style }} ref={ref}>
       {/* Pass width and height to children as render props */}
       {[props.children[0](width, height), props.children.slice(1)]}
     </div>
@@ -339,7 +426,8 @@ GrafanaGridItem.displayName = 'GridItemWithDimensions';
 function mapStateToProps() {
   return (state: StoreState) => ({
     isFnDashboard: state.fnGlobalState.FNDashboard,
+    isCustomDashboard: state.fnGlobalState.isCustomDashboard,
   });
 }
 
-export const DashboardGrid = connect(mapStateToProps)(Component);
+export const DashboardGrid = connect(mapStateToProps)(DashboardGridUnconnected);
diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx
index b2c34f0b392b8..b0873eabb0712 100644
--- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx
+++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx
@@ -58,7 +58,7 @@ export class DashboardPanelUnconnected extends PureComponent<Props> {
     }
   }
 
-  onInstanceStateChange = (value: any) => {
+  onInstanceStateChange = (value: unknown) => {
     this.props.setPanelInstanceState({ key: this.props.stateKey, value });
   };
 
diff --git a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx
index e6034feb9b124..f62fc3b3753eb 100644
--- a/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx
+++ b/public/app/features/dashboard/dashgrid/PanelStateWrapper.tsx
@@ -618,7 +618,7 @@ export class PanelStateWrapperDisConnected extends PureComponent<Props, State> {
 
 function mapStateToProps() {
   return (state: StoreState) => ({
-    isFnDashboard: state.fnGlobalState.FNDashboard,
+    isFnDashboard: state.fnGlobalState.FNDashboard && !state.fnGlobalState.isCustomDashboard,
   });
 }
 
diff --git a/public/app/fn-app/create-mfe.ts b/public/app/fn-app/create-mfe.ts
index d6661429f88bc..a5d196cc7e8fd 100644
--- a/public/app/fn-app/create-mfe.ts
+++ b/public/app/fn-app/create-mfe.ts
@@ -280,6 +280,7 @@ class createMfe {
             version: other.version,
             queryParams: other.queryParams,
             controlsContainer: other.controlsContainer,
+            isCustomDashboard: other.isCustomDashboard,
           })
         );
       }
diff --git a/public/app/fn-app/fn-app-provider.tsx b/public/app/fn-app/fn-app-provider.tsx
index 04d749ecd15c0..37d64c23aa485 100644
--- a/public/app/fn-app/fn-app-provider.tsx
+++ b/public/app/fn-app/fn-app-provider.tsx
@@ -1,10 +1,19 @@
+import { Action, KBarProvider } from 'kbar';
 import { useState, useEffect, FC, PropsWithChildren } from 'react';
 import { Provider } from 'react-redux';
-import { BrowserRouter } from 'react-router-dom';
+import { BrowserRouter, Router } from 'react-router-dom';
+import { CompatRouter } from 'react-router-dom-v5-compat';
 
-import { config, navigationLogger } from '@grafana/runtime';
+import {
+  config,
+  locationService,
+  LocationServiceProvider,
+  navigationLogger,
+  reportInteraction,
+} from '@grafana/runtime';
 import { ErrorBoundaryAlert, GlobalStyles } from '@grafana/ui';
 import { loadAndInitAngularIfEnabled } from 'app/angular/loadAndInitAngularIfEnabled';
+import { ModalsContextProvider } from 'app/core/context/ModalsContextProvider';
 import { ThemeProvider } from 'app/core/utils/ConfigProvider';
 import { FnLoader } from 'app/features/dashboard/components/DashboardLoading/FnLoader';
 import { FnLoggerService } from 'app/fn_logger';
@@ -31,6 +40,13 @@ export const FnAppProvider: FC<PropsWithChildren<FnAppProviderProps>> = (props)
       .catch(FnLoggerService.error);
   }, []);
 
+  const commandPaletteActionSelected = (action: Action) => {
+    reportInteraction('command_palette_action_selected', {
+      actionId: action.id,
+      actionName: action.name,
+    });
+  };
+
   if (!store || !ready) {
     return <FnLoader />;
   }
@@ -41,10 +57,21 @@ export const FnAppProvider: FC<PropsWithChildren<FnAppProviderProps>> = (props)
         <ErrorBoundaryAlert style="page">
           <GrafanaContext.Provider value={app.context}>
             <ThemeProvider value={config.theme2}>
-              <>
-                <GlobalStyles />
-                {children}
-              </>
+              <KBarProvider
+                actions={[]}
+                options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }}
+              >
+                <Router history={locationService.getHistory()}>
+                  <LocationServiceProvider service={locationService}>
+                    <CompatRouter>
+                      <ModalsContextProvider>
+                        <GlobalStyles />
+                        {children}
+                      </ModalsContextProvider>
+                    </CompatRouter>
+                  </LocationServiceProvider>
+                </Router>
+              </KBarProvider>
             </ThemeProvider>
           </GrafanaContext.Provider>
         </ErrorBoundaryAlert>
diff --git a/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx b/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
index e9dac0f5ce0cf..e6c2df45428b9 100644
--- a/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
+++ b/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
@@ -17,7 +17,7 @@ const DEFAULT_DASHBOARD_PAGE_PROPS: Pick<DashboardPageProps, 'history' | 'route'
     path: '/d/:uid/:slug?',
     url: '',
   },
-  history: {} as DashboardPageProps['history'],
+  history: locationService.getHistory(),
   route: {
     routeName: DashboardRoutes.Normal,
     path: '/d/:uid/:slug?',
diff --git a/public/app/fn-app/types.ts b/public/app/fn-app/types.ts
index 4a1bf55f26909..aa800f7032e49 100644
--- a/public/app/fn-app/types.ts
+++ b/public/app/fn-app/types.ts
@@ -30,5 +30,6 @@ export interface FNDashboardProps {
   isLoading: (isLoading: boolean) => void;
   setErrors: (errors?: { [K: number | string]: string }) => void;
   hiddenVariables: readonly string[];
+  isCustomDashboard?: boolean;
   container?: HTMLElement | null;
 }

From a42bf0a61b0a0e980656391e89697aa8225aa6f2 Mon Sep 17 00:00:00 2001
From: Gurinder Singh <gurinder@coderabbit.ai>
Date: Mon, 2 Dec 2024 11:36:36 -0500
Subject: [PATCH 2/5] fixes

---
 .../dashboard/containers/DashboardPage.tsx    |  14 +-
 .../dashboard/dashgrid/DashboardEmpty.tsx     | 147 ++++++++++--------
 2 files changed, 87 insertions(+), 74 deletions(-)

diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx
index 7c869e13be3eb..22165bc1b47a5 100644
--- a/public/app/features/dashboard/containers/DashboardPage.tsx
+++ b/public/app/features/dashboard/containers/DashboardPage.tsx
@@ -158,9 +158,9 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
 
   componentDidMount() {
     this.initDashboard();
-    const { FNDashboard, isCustomDashboard } = this.props;
+    const { FNDashboard } = this.props;
 
-    if (!FNDashboard || isCustomDashboard) {
+    if (!FNDashboard) {
       this.forceRouteReloadCounter = (this.props.history.location?.state as any)?.routeReloadCounter || 0;
     }
   }
@@ -198,13 +198,13 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
   }
 
   componentDidUpdate(prevProps: Props, prevState: State) {
-    const { dashboard, match, templateVarsChangedInUrl, FNDashboard, isCustomDashboard } = this.props;
+    const { dashboard, match, templateVarsChangedInUrl, FNDashboard } = this.props;
 
     if (!dashboard) {
       return;
     }
 
-    if (!FNDashboard || isCustomDashboard) {
+    if (!FNDashboard) {
       const routeReloadCounter = (this.props.history.location?.state as any)?.routeReloadCounter;
 
       if (
@@ -407,6 +407,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
       configToggle: config.featureToggles.vizAndWidgetSplit,
     });
 
+
     const pageClassName = cx({
       'panel-in-fullscreen': Boolean(viewPanel),
       'page-hidden': Boolean(queryParams.editview || editPanel),
@@ -440,6 +441,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
           layout={PageLayoutType.Canvas}
           className={pageClassName}
           onSetScrollRef={this.setScrollRef}
+          style={{ minHeight: 600 }}
         >
           {showToolbar && (
             <header data-testid={selectors.pages.Dashboard.DashNav.navV2}>
@@ -489,7 +491,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
               )}
             </header>
           )}
-          {!FNDashboard && <DashboardPrompt dashboard={dashboard} />}
+          {!isFNDashboardEditable && <DashboardPrompt dashboard={dashboard} />}
           {initError && <DashboardFailed />}
           {showSubMenu && (
             <section aria-label={selectors.pages.Dashboard.SubMenu.submenu}>
@@ -503,7 +505,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
             editPanel={editPanel}
           />
 
-          {inspectPanel && !FNDashboard && <PanelInspector dashboard={dashboard} panel={inspectPanel} />}
+          {inspectPanel && isFNDashboardEditable && <PanelInspector dashboard={dashboard} panel={inspectPanel} />}
         </Page>
         {editPanel && sectionNav && pageNav && isFNDashboardEditable && (
           <PanelEditor
diff --git a/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx b/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx
index e8065787e83d9..a7225245d8c57 100644
--- a/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx
+++ b/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx
@@ -26,7 +26,10 @@ export interface Props {
 const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
   const styles = useStyles2(getStyles);
   const dispatch = useDispatch();
-  const initialDatasource = useSelector((state) => state.dashboard.initialDatasource);
+  const { initialDatasource, isFNDashboard } = useSelector((state) => ({
+    initialDatasource: state.dashboard.initialDatasource,
+    isFNDashboard: state.fnGlobalState.FNDashboard
+  }));
 
   const onAddVisualization = () => {
     let id;
@@ -63,14 +66,18 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
                   Start your new dashboard by adding a visualization
                 </Trans>
               </Text>
-              <Box marginBottom={2} paddingX={4}>
-                <Text element="p" textAlignment="center" color="secondary">
-                  <Trans i18nKey="dashboard.empty.add-visualization-body">
-                    Select a data source and then query and visualize your data with charts, stats and tables or create
-                    lists, markdowns and other widgets.
-                  </Trans>
-                </Text>
-              </Box>
+
+                  <Box marginBottom={2} paddingX={4}>
+                  <Text element="p" textAlignment="center" color="secondary">
+                    {
+                      !isFNDashboard ? <Trans i18nKey="dashboard.empty.add-visualization-body">
+                        Select a data source and then query and visualize your data with charts, stats and tables or create
+                        lists, markdowns and other widgets.
+                      </Trans>: `Visualize CodeRabbit Review metrics using charts, tables and other widgets.`
+                    }
+                  </Text>
+                </Box>
+
               <Button
                 size="lg"
                 icon="plus"
@@ -82,83 +89,87 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
               </Button>
             </Stack>
           </Box>
-          <Stack direction={{ xs: 'column', md: 'row' }} wrap="wrap" gap={4}>
-            {config.featureToggles.vizAndWidgetSplit && (
+          {
+            !isFNDashboard && (
+              <Stack direction={{ xs: 'column', md: 'row' }} wrap="wrap" gap={4}>
+              {config.featureToggles.vizAndWidgetSplit && (
+                <Box borderColor="strong" borderStyle="dashed" padding={3} flex={1}>
+                  <Stack direction="column" alignItems="center" gap={1}>
+                    <Text element="h3" textAlignment="center" weight="medium">
+                      <Trans i18nKey="dashboard.empty.add-widget-header">Add a widget</Trans>
+                    </Text>
+                    <Box marginBottom={2}>
+                      <Text element="p" textAlignment="center" color="secondary">
+                        <Trans i18nKey="dashboard.empty.add-widget-body">Create lists, markdowns and other widgets</Trans>
+                      </Text>
+                    </Box>
+                    <Button
+                      icon="plus"
+                      fill="outline"
+                      data-testid={selectors.pages.AddDashboard.itemButton('Create new widget button')}
+                      onClick={() => {
+                        DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_widget' });
+                        locationService.partial({ addWidget: true });
+                      }}
+                      disabled={!canCreate}
+                    >
+                      <Trans i18nKey="dashboard.empty.add-widget-button">Add widget</Trans>
+                    </Button>
+                  </Stack>
+                </Box>
+              )}
               <Box borderColor="strong" borderStyle="dashed" padding={3} flex={1}>
                 <Stack direction="column" alignItems="center" gap={1}>
                   <Text element="h3" textAlignment="center" weight="medium">
-                    <Trans i18nKey="dashboard.empty.add-widget-header">Add a widget</Trans>
+                    <Trans i18nKey="dashboard.empty.add-library-panel-header">Import panel</Trans>
                   </Text>
                   <Box marginBottom={2}>
                     <Text element="p" textAlignment="center" color="secondary">
-                      <Trans i18nKey="dashboard.empty.add-widget-body">Create lists, markdowns and other widgets</Trans>
+                      <Trans i18nKey="dashboard.empty.add-library-panel-body">
+                        Add visualizations that are shared with other dashboards.
+                      </Trans>
                     </Text>
                   </Box>
                   <Button
                     icon="plus"
                     fill="outline"
-                    data-testid={selectors.pages.AddDashboard.itemButton('Create new widget button')}
+                    data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
+                    onClick={onAddLibraryPanel}
+                    disabled={!canCreate}
+                  >
+                    <Trans i18nKey="dashboard.empty.add-library-panel-button">Add library panel</Trans>
+                  </Button>
+                </Stack>
+              </Box>
+              <Box borderColor="strong" borderStyle="dashed" padding={3} flex={1}>
+                <Stack direction="column" alignItems="center" gap={1}>
+                  <Text element="h3" textAlignment="center" weight="medium">
+                    <Trans i18nKey="dashboard.empty.import-a-dashboard-header">Import a dashboard</Trans>
+                  </Text>
+                  <Box marginBottom={2}>
+                    <Text element="p" textAlignment="center" color="secondary">
+                      <Trans i18nKey="dashboard.empty.import-a-dashboard-body">
+                        Import dashboards from files or <a href="https://grafana.com/grafana/dashboards/">grafana.com</a>.
+                      </Trans>
+                    </Text>
+                  </Box>
+                  <Button
+                    icon="upload"
+                    fill="outline"
+                    data-testid={selectors.pages.AddDashboard.itemButton('Import dashboard button')}
                     onClick={() => {
-                      DashboardInteractions.emptyDashboardButtonClicked({ item: 'add_widget' });
-                      locationService.partial({ addWidget: true });
+                      DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_dashboard' });
+                      onImportDashboard();
                     }}
                     disabled={!canCreate}
                   >
-                    <Trans i18nKey="dashboard.empty.add-widget-button">Add widget</Trans>
+                    <Trans i18nKey="dashboard.empty.import-dashboard-button">Import dashboard</Trans>
                   </Button>
                 </Stack>
               </Box>
-            )}
-            <Box borderColor="strong" borderStyle="dashed" padding={3} flex={1}>
-              <Stack direction="column" alignItems="center" gap={1}>
-                <Text element="h3" textAlignment="center" weight="medium">
-                  <Trans i18nKey="dashboard.empty.add-library-panel-header">Import panel</Trans>
-                </Text>
-                <Box marginBottom={2}>
-                  <Text element="p" textAlignment="center" color="secondary">
-                    <Trans i18nKey="dashboard.empty.add-library-panel-body">
-                      Add visualizations that are shared with other dashboards.
-                    </Trans>
-                  </Text>
-                </Box>
-                <Button
-                  icon="plus"
-                  fill="outline"
-                  data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
-                  onClick={onAddLibraryPanel}
-                  disabled={!canCreate}
-                >
-                  <Trans i18nKey="dashboard.empty.add-library-panel-button">Add library panel</Trans>
-                </Button>
-              </Stack>
-            </Box>
-            <Box borderColor="strong" borderStyle="dashed" padding={3} flex={1}>
-              <Stack direction="column" alignItems="center" gap={1}>
-                <Text element="h3" textAlignment="center" weight="medium">
-                  <Trans i18nKey="dashboard.empty.import-a-dashboard-header">Import a dashboard</Trans>
-                </Text>
-                <Box marginBottom={2}>
-                  <Text element="p" textAlignment="center" color="secondary">
-                    <Trans i18nKey="dashboard.empty.import-a-dashboard-body">
-                      Import dashboards from files or <a href="https://grafana.com/grafana/dashboards/">grafana.com</a>.
-                    </Trans>
-                  </Text>
-                </Box>
-                <Button
-                  icon="upload"
-                  fill="outline"
-                  data-testid={selectors.pages.AddDashboard.itemButton('Import dashboard button')}
-                  onClick={() => {
-                    DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_dashboard' });
-                    onImportDashboard();
-                  }}
-                  disabled={!canCreate}
-                >
-                  <Trans i18nKey="dashboard.empty.import-dashboard-button">Import dashboard</Trans>
-                </Button>
-              </Stack>
-            </Box>
-          </Stack>
+            </Stack>
+            )
+          }
         </Stack>
       </div>
     </Stack>

From c602459abcfc337df02e1bb6efb811a972f0419a Mon Sep 17 00:00:00 2001
From: Gurinder Singh <gurinder@coderabbit.ai>
Date: Mon, 2 Dec 2024 22:36:24 -0500
Subject: [PATCH 3/5] checkpoint

---
 .../dashboard/containers/DashboardPage.tsx    | 14 +-----
 public/app/fn-app/fn-app-provider.tsx         |  9 +++-
 .../fn-dashboard-page/render-fn-dashboard.tsx | 49 ++++++++++++-------
 3 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx
index 22165bc1b47a5..9d396536083d4 100644
--- a/public/app/features/dashboard/containers/DashboardPage.tsx
+++ b/public/app/features/dashboard/containers/DashboardPage.tsx
@@ -274,17 +274,16 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
   };
 
   static getDerivedStateFromProps(props: Props, state: State) {
-    const { dashboard, queryParams } = props;
+    const { dashboard, queryParams, isCustomDashboard, FNDashboard } = props;
 
     const urlEditPanelId = queryParams.editPanel;
     const urlViewPanelId = queryParams.viewPanel;
 
-    if (!dashboard) {
+    if (!dashboard || (FNDashboard && !isCustomDashboard)) {
       return state;
     }
 
     const updatedState = { ...state };
-
     // Entering edit mode
     if (!state.editPanel && urlEditPanelId) {
       const panel = dashboard.getPanelByUrlId(urlEditPanelId);
@@ -399,15 +398,6 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
 
     const isFNDashboardEditable = (isCustomDashboard && FNDashboard) || !FNDashboard;
 
-    console.log('Edit Panel: ', { editPanel, sectionNav, pageNav, isFNDashboardEditable });
-    console.log('Dashboard settings: ', { editView: queryParams.editview, pageNav, sectionNav, isFNDashboardEditable });
-    console.log('Add Widget: ', {
-      isFNDashboardEditable,
-      addWidget: queryParams.addWidget,
-      configToggle: config.featureToggles.vizAndWidgetSplit,
-    });
-
-
     const pageClassName = cx({
       'panel-in-fullscreen': Boolean(viewPanel),
       'page-hidden': Boolean(queryParams.editview || editPanel),
diff --git a/public/app/fn-app/fn-app-provider.tsx b/public/app/fn-app/fn-app-provider.tsx
index 37d64c23aa485..b9392fe9147b1 100644
--- a/public/app/fn-app/fn-app-provider.tsx
+++ b/public/app/fn-app/fn-app-provider.tsx
@@ -12,7 +12,9 @@ import {
   reportInteraction,
 } from '@grafana/runtime';
 import { ErrorBoundaryAlert, GlobalStyles } from '@grafana/ui';
+import { AngularRoot } from 'app/angular/AngularRoot';
 import { loadAndInitAngularIfEnabled } from 'app/angular/loadAndInitAngularIfEnabled';
+import { AppChrome } from 'app/core/components/AppChrome/AppChrome';
 import { ModalsContextProvider } from 'app/core/context/ModalsContextProvider';
 import { ThemeProvider } from 'app/core/utils/ConfigProvider';
 import { FnLoader } from 'app/features/dashboard/components/DashboardLoading/FnLoader';
@@ -66,7 +68,12 @@ export const FnAppProvider: FC<PropsWithChildren<FnAppProviderProps>> = (props)
                     <CompatRouter>
                       <ModalsContextProvider>
                         <GlobalStyles />
-                        {children}
+                        <div className="grafana-app">
+                          <AppChrome>
+                            <AngularRoot />
+                            {children}
+                          </AppChrome>
+                        </div>
                       </ModalsContextProvider>
                     </CompatRouter>
                   </LocationServiceProvider>
diff --git a/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx b/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
index e6c2df45428b9..05d5206261aa4 100644
--- a/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
+++ b/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
@@ -1,4 +1,4 @@
-import { merge, isFunction } from 'lodash';
+import { merge, isFunction, isEqual } from 'lodash';
 import { useEffect, FC, useMemo } from 'react';
 
 import { locationService as locationSrv, HistoryWrapper } from '@grafana/runtime';
@@ -50,26 +50,39 @@ export const RenderFNDashboard: FC<FNDashboardProps> = (props) => {
   }, [firstError, setErrors]);
 
   useEffect(() => {
-    locationService.fnPathnameChange(window.location.pathname, queryParams);
+    const searchParams = getSearchParamsObject();
+    if (isEqual(searchParams, queryParams)) {
+      return;
+    }
+    locationService.fnPathnameChange(window.location.pathname, {
+      ...searchParams,
+      ...queryParams,
+    });
   }, [queryParams]);
 
-  const dashboardPageProps: DashboardPageProps = useMemo(
-    () =>
-      merge({}, DEFAULT_DASHBOARD_PAGE_PROPS, {
-        ...DEFAULT_DASHBOARD_PAGE_PROPS,
-        match: {
-          params: {
-            ...props,
-          },
+  const dashboardPageProps: DashboardPageProps = useMemo(() => {
+    return merge({}, DEFAULT_DASHBOARD_PAGE_PROPS, {
+      ...DEFAULT_DASHBOARD_PAGE_PROPS,
+      match: {
+        params: {
+          ...props,
         },
-        location: locationService.getLocation(),
-        queryParams,
-        hiddenVariables,
-        controlsContainer,
-        isLoading,
-      }),
-    [controlsContainer, hiddenVariables, isLoading, props, queryParams]
-  );
+      },
+      location: locationService.getLocation(),
+      queryParams: {
+        ...getSearchParamsObject(),
+        ...queryParams,
+      },
+      hiddenVariables,
+      controlsContainer,
+      isLoading,
+    });
+  }, [controlsContainer, hiddenVariables, isLoading, props, queryParams]);
 
   return <DashboardPage {...dashboardPageProps} />;
 };
+
+function getSearchParamsObject() {
+  const searchParams = new URLSearchParams(window.location.search);
+  return Object.fromEntries(searchParams.entries());
+}

From 38e481cb1e47efce3c004049ab8be4244832cb71 Mon Sep 17 00:00:00 2001
From: Gurinder Singh <gurinder@coderabbit.ai>
Date: Wed, 4 Dec 2024 19:38:28 -0500
Subject: [PATCH 4/5] checkpoint

---
 .../components/PanelEditor/PanelEditor.tsx    | 52 ++++++++++++++-----
 .../dashboard/containers/DashboardPage.tsx    |  6 ++-
 public/app/fn-app/fn-app-provider.tsx         |  3 +-
 .../fn-app/fn-dashboard-page/fn-dashboard.tsx |  2 +-
 4 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
index a77c269d0ca0e..9299447f5b8cb 100644
--- a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
@@ -72,6 +72,8 @@ const mapStateToProps = (state: StoreState, ownProps: OwnProps) => {
     uiState: state.panelEditor.ui,
     tableViewEnabled: state.panelEditor.tableViewEnabled,
     variables: getVariablesByKey(ownProps.dashboard.uid, state),
+    isMFEDashboard: state.fnGlobalState.FNDashboard,
+    isMFECustomDashboard: state.fnGlobalState.isCustomDashboard,
   };
 };
 
@@ -431,27 +433,33 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
   };
 
   render() {
-    const { initDone, uiState, theme, sectionNav, pageNav, className, updatePanelEditorUIState } = this.props;
+    const { initDone, uiState, theme, sectionNav, pageNav, className, updatePanelEditorUIState, isMFECustomDashboard } = this.props;
     const styles = getStyles(theme, this.props);
 
     if (!initDone) {
       return null;
     }
 
-    return (
-      <Page
-        navModel={sectionNav}
-        pageNav={pageNav}
-        data-testid={selectors.components.PanelEditor.General.content}
-        layout={PageLayoutType.Custom}
-        className={className}
-      >
-        <AppChromeUpdate
+    console.log('PanelEditor.tsx this.props', {
+      isPanelOptionVisible: uiState.isPanelOptionsVisible,
+      isMFECustomDashboard,
+      modelState: this.state.showSaveLibraryPanelModal,
+    });
+
+    const editPanelContent = (
+      <>
+      {
+        !isMFECustomDashboard ? (
+          <AppChromeUpdate
           actions={<ToolbarButtonRow alignment="right">{this.renderEditorActions()}</ToolbarButtonRow>}
-        />
+          />
+        ): (
+          <ToolbarButtonRow alignment="right">{this.renderEditorActions()}</ToolbarButtonRow>
+        )
+      }
         <div className={styles.wrapper}>
           <div className={styles.verticalSplitPanesWrapper}>
-            {!uiState.isPanelOptionsVisible ? (
+            {!uiState.isPanelOptionsVisible || isMFECustomDashboard ? (
               this.renderPanelAndEditor(uiState, styles)
             ) : (
               <SplitPaneWrapper
@@ -480,7 +488,25 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
             />
           )}
         </div>
-      </Page>
+      </>
+    )
+
+    return (
+      <>
+      {
+        !isMFECustomDashboard ? (
+          <Page
+          navModel={sectionNav}
+          pageNav={pageNav}
+          data-testid={selectors.components.PanelEditor.General.content}
+          layout={PageLayoutType.Custom}
+          className={className}
+        >
+        {editPanelContent}
+        </Page>
+        ): <div>{editPanelContent}</div>
+      }
+      </>
     );
   }
 }
diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx
index 9d396536083d4..1b086ed812084 100644
--- a/public/app/features/dashboard/containers/DashboardPage.tsx
+++ b/public/app/features/dashboard/containers/DashboardPage.tsx
@@ -423,6 +423,8 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
       </Portal>
     );
 
+    const isPanelEditorVisible = editPanel && sectionNav && pageNav && isFNDashboardEditable
+
     return (
       <>
         <Page
@@ -431,7 +433,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
           layout={PageLayoutType.Canvas}
           className={pageClassName}
           onSetScrollRef={this.setScrollRef}
-          style={{ minHeight: 600 }}
+          style={{ minHeight: 600, position: 'relative' }}
         >
           {showToolbar && (
             <header data-testid={selectors.pages.Dashboard.DashNav.navV2}>
@@ -497,7 +499,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
 
           {inspectPanel && isFNDashboardEditable && <PanelInspector dashboard={dashboard} panel={inspectPanel} />}
         </Page>
-        {editPanel && sectionNav && pageNav && isFNDashboardEditable && (
+        {isPanelEditorVisible && (
           <PanelEditor
             dashboard={dashboard}
             sourcePanel={editPanel}
diff --git a/public/app/fn-app/fn-app-provider.tsx b/public/app/fn-app/fn-app-provider.tsx
index b9392fe9147b1..01fcf0616c155 100644
--- a/public/app/fn-app/fn-app-provider.tsx
+++ b/public/app/fn-app/fn-app-provider.tsx
@@ -11,7 +11,7 @@ import {
   navigationLogger,
   reportInteraction,
 } from '@grafana/runtime';
-import { ErrorBoundaryAlert, GlobalStyles } from '@grafana/ui';
+import { ErrorBoundaryAlert, GlobalStyles, ModalRoot } from '@grafana/ui';
 import { AngularRoot } from 'app/angular/AngularRoot';
 import { loadAndInitAngularIfEnabled } from 'app/angular/loadAndInitAngularIfEnabled';
 import { AppChrome } from 'app/core/components/AppChrome/AppChrome';
@@ -74,6 +74,7 @@ export const FnAppProvider: FC<PropsWithChildren<FnAppProviderProps>> = (props)
                             {children}
                           </AppChrome>
                         </div>
+                        <ModalRoot />
                       </ModalsContextProvider>
                     </CompatRouter>
                   </LocationServiceProvider>
diff --git a/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx b/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
index 93c50e5d3c52a..77f77f1191317 100644
--- a/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
+++ b/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
@@ -54,7 +54,7 @@ export const DashboardPortal: FC<FNDashboardComponentProps> = (p) => {
 
   return (
     <RenderPortal ID="grafana-portal">
-      <div className="page-dashboard">{content}</div>
+      {content}
     </RenderPortal>
   );
 };

From d57e22e3971937f1d138107e710ff11ac7aa8c1a Mon Sep 17 00:00:00 2001
From: Gurinder Singh <gurinder@coderabbit.ai>
Date: Thu, 12 Dec 2024 16:56:23 -0500
Subject: [PATCH 5/5] checkpoint

---
 .../src/components/Portal/Portal.tsx          |  1 +
 public/app/AppWrapper.tsx                     | 35 +++++++-----
 public/app/app.ts                             | 32 ++++++++---
 public/app/core/context/ModalsProvider.ts     |  0
 .../components/PanelEditor/PanelEditor.tsx    | 56 +++++++------------
 .../PanelEditor/PanelEditorTabs.tsx           |  9 +--
 .../dashboard/containers/DashboardPage.tsx    |  2 +-
 .../features/query/components/QueryGroup.tsx  | 10 +++-
 public/app/fn-app/create-mfe.ts               |  4 +-
 .../fn-app/fn-dashboard-page/fn-dashboard.tsx | 10 +++-
 .../fn-dashboard-page/render-fn-dashboard.tsx | 16 ++++--
 11 files changed, 103 insertions(+), 72 deletions(-)
 delete mode 100644 public/app/core/context/ModalsProvider.ts

diff --git a/packages/grafana-ui/src/components/Portal/Portal.tsx b/packages/grafana-ui/src/components/Portal/Portal.tsx
index f4f88831e72e8..62ac76b6a4f1c 100644
--- a/packages/grafana-ui/src/components/Portal/Portal.tsx
+++ b/packages/grafana-ui/src/components/Portal/Portal.tsx
@@ -60,6 +60,7 @@ export function PortalContainer() {
   return (
     <div
       id="grafana-portal-container"
+      data-qiankun="grafana-full-app"
       className={cx({
         [styles.grafanaPortalContainer]: isBodyScrolling,
       })}
diff --git a/public/app/AppWrapper.tsx b/public/app/AppWrapper.tsx
index b73839203c2cb..99d9f6a2f4b9a 100644
--- a/public/app/AppWrapper.tsx
+++ b/public/app/AppWrapper.tsx
@@ -30,6 +30,8 @@ import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
 
 interface AppWrapperProps {
   app: GrafanaApp;
+  isMFE?: boolean;
+  children?: React.ReactNode | null;
 }
 
 interface AppWrapperState {
@@ -88,7 +90,7 @@ export class AppWrapper extends Component<AppWrapperProps, AppWrapperState> {
   }
 
   render() {
-    const { app } = this.props;
+    const { app, isMFE, children } = this.props;
     const { ready } = this.state;
 
     navigationLogger('AppWrapper', false, 'rendering');
@@ -116,22 +118,29 @@ export class AppWrapper extends Component<AppWrapperProps, AppWrapperState> {
                         <GlobalStyles />
                         <div className="grafana-app">
                           <AppChrome>
-                            <AngularRoot />
-                            <AppNotificationList />
-                            <Stack gap={0} grow={1} direction="column">
-                              {pageBanners.map((Banner, index) => (
-                                <Banner key={index.toString()} />
+                            <>
+                              <AngularRoot />
+                              <AppNotificationList />
+                              <Stack gap={0} grow={1} direction="column">
+                                {pageBanners.map((Banner, index) => (
+                                  <Banner key={index.toString()} />
+                                ))}
+                                {ready && !isMFE && this.renderRoutes()}
+                              </Stack>
+                              {bodyRenderHooks.map((Hook, index) => (
+                                <Hook key={index.toString()} />
                               ))}
-                              {ready && this.renderRoutes()}
-                            </Stack>
-                            {bodyRenderHooks.map((Hook, index) => (
-                              <Hook key={index.toString()} />
-                            ))}
+                            </>
+                            {children}
                           </AppChrome>
                         </div>
                         <LiveConnectionWarning />
-                        <ModalRoot />
-                        <PortalContainer />
+                        {!isMFE && (
+                          <>
+                            <ModalRoot />
+                            <PortalContainer />
+                          </>
+                        )}
                       </ModalsContextProvider>
                     </CompatRouter>
                   </LocationServiceProvider>
diff --git a/public/app/app.ts b/public/app/app.ts
index 278884385f3d5..2ebe82e9e1729 100644
--- a/public/app/app.ts
+++ b/public/app/app.ts
@@ -44,7 +44,7 @@ import {
 import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView';
 import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer';
 import { setPluginPage } from '@grafana/runtime/src/components/PluginPage';
-import config, { updateConfig } from 'app/core/config';
+import config, { Settings, updateConfig } from 'app/core/config';
 import { arrayMove } from 'app/core/utils/arrayMove';
 import { getStandardTransformers } from 'app/features/transformers/standardTransformers';
 
@@ -125,7 +125,7 @@ if (process.env.NODE_ENV === 'development') {
 export class GrafanaApp {
   context!: GrafanaContextType;
 
-  async init() {
+  async init(isMFE = false) {
     try {
       // Let iframe container know grafana has started loading
       parent.postMessage('GrafanaAppInit', '*');
@@ -133,7 +133,19 @@ export class GrafanaApp {
       const initI18nPromise = initializeI18n(config.bootData.user.language);
       initI18nPromise.then(({ language }) => updateConfig({ language }));
 
-      setBackendSrv(backendSrv);
+      if(isMFE){
+        backendSrv.setGrafanaPrefix(true);
+        setBackendSrv(backendSrv);
+        const settings: Settings = await backendSrv.get('/api/frontend/settings');
+
+        config.panels = settings.panels;
+        config.datasources = settings.datasources;
+        config.defaultDatasource = settings.defaultDatasource;
+      } else {
+        setBackendSrv(backendSrv);
+      }
+
+
       initEchoSrv();
       initIconCache();
       // This needs to be done after the `initEchoSrv` since it is being used under the hood.
@@ -270,12 +282,14 @@ export class GrafanaApp {
 
       initializeScopes();
 
-      const root = createRoot(document.getElementById('reactRoot')!);
-      root.render(
-        createElement(AppWrapper, {
-          app: this,
-        })
-      );
+      if(!isMFE){
+        const root = createRoot(document.getElementById('reactRoot')!);
+        root.render(
+          createElement(AppWrapper, {
+            app: this,
+          })
+        );
+      }
     } catch (error) {
       console.error('Failed to start Grafana', error);
       window.__grafana_load_failed();
diff --git a/public/app/core/context/ModalsProvider.ts b/public/app/core/context/ModalsProvider.ts
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
index 9299447f5b8cb..c95d0c6e66cfc 100644
--- a/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/PanelEditor.tsx
@@ -277,6 +277,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
             dashboard={dashboard}
             tabs={tabs}
             onChangeTab={this.onChangeTab}
+            isMfeEditPanel={this.props.isMFEDashboard}
           />
         </div>
       </SplitPaneWrapper>
@@ -433,33 +434,32 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
   };
 
   render() {
-    const { initDone, uiState, theme, sectionNav, pageNav, className, updatePanelEditorUIState, isMFECustomDashboard } = this.props;
+    const { initDone, uiState, theme, sectionNav, pageNav, className, updatePanelEditorUIState, isMFECustomDashboard } =
+      this.props;
     const styles = getStyles(theme, this.props);
 
     if (!initDone) {
       return null;
     }
 
-    console.log('PanelEditor.tsx this.props', {
-      isPanelOptionVisible: uiState.isPanelOptionsVisible,
-      isMFECustomDashboard,
-      modelState: this.state.showSaveLibraryPanelModal,
-    });
-
-    const editPanelContent = (
-      <>
-      {
-        !isMFECustomDashboard ? (
+    return (
+      <Page
+        navModel={sectionNav}
+        pageNav={pageNav}
+        data-testid={selectors.components.PanelEditor.General.content}
+        layout={PageLayoutType.Custom}
+        className={!isMFECustomDashboard ? className : styles.mfeWrapper}
+      >
+        {!isMFECustomDashboard ? (
           <AppChromeUpdate
-          actions={<ToolbarButtonRow alignment="right">{this.renderEditorActions()}</ToolbarButtonRow>}
+            actions={<ToolbarButtonRow alignment="right">{this.renderEditorActions()}</ToolbarButtonRow>}
           />
-        ): (
+        ) : (
           <ToolbarButtonRow alignment="right">{this.renderEditorActions()}</ToolbarButtonRow>
-        )
-      }
+        )}
         <div className={styles.wrapper}>
           <div className={styles.verticalSplitPanesWrapper}>
-            {!uiState.isPanelOptionsVisible || isMFECustomDashboard ? (
+            {!uiState.isPanelOptionsVisible ? (
               this.renderPanelAndEditor(uiState, styles)
             ) : (
               <SplitPaneWrapper
@@ -488,25 +488,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
             />
           )}
         </div>
-      </>
-    )
-
-    return (
-      <>
-      {
-        !isMFECustomDashboard ? (
-          <Page
-          navModel={sectionNav}
-          pageNav={pageNav}
-          data-testid={selectors.components.PanelEditor.General.content}
-          layout={PageLayoutType.Custom}
-          className={className}
-        >
-        {editPanelContent}
-        </Page>
-        ): <div>{editPanelContent}</div>
-      }
-      </>
+      </Page>
     );
   }
 }
@@ -521,12 +503,16 @@ export const getStyles = stylesFactory((theme: GrafanaTheme2, props: Props) => {
   const paneSpacing = theme.spacing(2);
 
   return {
+    mfeWrapper: css({
+      height: '100vh',
+    }),
     wrapper: css({
       width: '100%',
       flexGrow: 1,
       minHeight: 0,
       display: 'flex',
       paddingTop: theme.spacing(2),
+      height: '100%',
     }),
     verticalSplitPanesWrapper: css({
       display: 'flex',
diff --git a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
index cad04df45f339..33240377edf1b 100644
--- a/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
+++ b/public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
@@ -20,9 +20,10 @@ interface PanelEditorTabsProps {
   dashboard: DashboardModel;
   tabs: PanelEditorTab[];
   onChangeTab: (tab: PanelEditorTab) => void;
+  isMfeEditPanel?: boolean;
 }
 
-export const PanelEditorTabs = memo(({ panel, dashboard, tabs, onChangeTab }: PanelEditorTabsProps) => {
+export const PanelEditorTabs = memo(({ panel, dashboard, tabs, onChangeTab, isMfeEditPanel }: PanelEditorTabsProps) => {
   const forceUpdate = useForceUpdate();
   const styles = useStyles2(getStyles);
 
@@ -49,7 +50,7 @@ export const PanelEditorTabs = memo(({ panel, dashboard, tabs, onChangeTab }: Pa
     return () => eventSubs.unsubscribe();
   }, [panel, dashboard, forceUpdate]);
 
-  const activeTab = tabs.find((item) => item.active)!;
+  const activeTab = !isMfeEditPanel? tabs.find((item) => item.active)!: tabs.find((item) => item.id === PanelEditorTabId.Query)!;
 
   if (tabs.length === 0) {
     return null;
@@ -60,7 +61,7 @@ export const PanelEditorTabs = memo(({ panel, dashboard, tabs, onChangeTab }: Pa
   return (
     <div className={styles.wrapper}>
       <TabsBar className={styles.tabBar} hideBorder>
-        {tabs.map((tab) => {
+        {!isMfeEditPanel ? tabs.map((tab) => {
           if (tab.id === PanelEditorTabId.Alert && alertingEnabled) {
             return (
               <PanelAlertTab
@@ -84,7 +85,7 @@ export const PanelEditorTabs = memo(({ panel, dashboard, tabs, onChangeTab }: Pa
               counter={getCounter(panel, tab)}
             />
           );
-        })}
+        }): null}
       </TabsBar>
       <TabContent className={styles.tabContent}>
         {activeTab.id === PanelEditorTabId.Query && <PanelEditorQueries panel={panel} queries={panel.targets} />}
diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx
index 1b086ed812084..01983604dc0bf 100644
--- a/public/app/features/dashboard/containers/DashboardPage.tsx
+++ b/public/app/features/dashboard/containers/DashboardPage.tsx
@@ -483,7 +483,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
               )}
             </header>
           )}
-          {!isFNDashboardEditable && <DashboardPrompt dashboard={dashboard} />}
+          <DashboardPrompt dashboard={dashboard} />
           {initError && <DashboardFailed />}
           {showSubMenu && (
             <section aria-label={selectors.pages.Dashboard.SubMenu.submenu}>
diff --git a/public/app/features/query/components/QueryGroup.tsx b/public/app/features/query/components/QueryGroup.tsx
index ffa97a3fce235..943bc721c74d2 100644
--- a/public/app/features/query/components/QueryGroup.tsx
+++ b/public/app/features/query/components/QueryGroup.tsx
@@ -27,7 +27,7 @@ import { dataSource as expressionDatasource } from 'app/features/expressions/Exp
 import { AngularDeprecationPluginNotice } from 'app/features/plugins/angularDeprecation/AngularDeprecationPluginNotice';
 import { isSharedDashboardQuery } from 'app/plugins/datasource/dashboard';
 import { GrafanaQuery } from 'app/plugins/datasource/grafana/types';
-import { QueryGroupOptions } from 'app/types';
+import { QueryGroupOptions, StoreState, useSelector } from 'app/types';
 
 import { isAngularDatasourcePluginAndNotHidden } from '../../plugins/angularDeprecation/utils';
 import { PanelQueryRunner } from '../state/PanelQueryRunner';
@@ -37,6 +37,7 @@ import { GroupActionComponents } from './QueryActionComponent';
 import { QueryEditorRows } from './QueryEditorRows';
 import { QueryGroupOptionsEditor } from './QueryGroupOptions';
 
+
 export interface Props {
   queryRunner: PanelQueryRunner;
   options: QueryGroupOptions;
@@ -404,6 +405,13 @@ export function QueryGroupTopSection({
 }: QueryGroupTopSectionProps) {
   const styles = getStyles();
   const [isHelpOpen, setIsHelpOpen] = useState(false);
+  const { FNDashboard, isCustomDashboard } = useSelector((state: StoreState) => state.fnGlobalState);
+
+  // do not render data source selection options in micro frontend dashboard
+  if(isCustomDashboard && FNDashboard){
+    return null;
+  }
+
   return (
     <>
       <div data-testid={selectors.components.QueryTab.queryGroupTopSection}>
diff --git a/public/app/fn-app/create-mfe.ts b/public/app/fn-app/create-mfe.ts
index a5d196cc7e8fd..47b84e8006fc4 100644
--- a/public/app/fn-app/create-mfe.ts
+++ b/public/app/fn-app/create-mfe.ts
@@ -14,6 +14,7 @@ import { GrafanaTheme2 } from '@grafana/data/src/themes/types';
 import { ThemeChangedEvent } from '@grafana/runtime';
 import { GrafanaBootConfig } from '@grafana/runtime/src/config';
 import { getTheme } from '@grafana/ui';
+import app from 'app/app';
 import appEvents from 'app/core/app_events';
 import config from 'app/core/config';
 import {
@@ -25,7 +26,6 @@ import {
   fnStateProps,
 } from 'app/core/reducers/fn-slice';
 import { backendSrv } from 'app/core/services/backend_srv';
-import fn_app from 'app/fn_app';
 import { FnLoggerService } from 'app/fn_logger';
 import { dispatch } from 'app/store/store';
 
@@ -89,7 +89,7 @@ class createMfe {
   }
 
   static boot() {
-    return () => fn_app.init();
+    return () => app.init(true);
   }
 
   private static toggleTheme = (mode: FNDashboardProps['mode']): GrafanaThemeType.Light | GrafanaThemeType.Dark =>
diff --git a/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx b/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
index 77f77f1191317..e27ff542d8aff 100644
--- a/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
+++ b/public/app/fn-app/fn-dashboard-page/fn-dashboard.tsx
@@ -1,9 +1,11 @@
 import { FC, useMemo } from 'react';
 
+import { ModalRoot, PortalContainer } from '@grafana/ui';
+import { AppWrapper } from 'app/AppWrapper';
+import app from 'app/app';
 import { FnGlobalState, FnPropMappedFromState } from 'app/core/reducers/fn-slice';
 import { useSelector } from 'app/types';
 
-import { FnAppProvider } from '../fn-app-provider';
 import { FNDashboardProps } from '../types';
 import { RenderPortal } from '../utils';
 
@@ -13,9 +15,9 @@ type FNDashboardComponentProps = Omit<FNDashboardProps, FnPropMappedFromState>;
 
 export const FNDashboard: FC<FNDashboardComponentProps> = (props) => {
   return (
-    <FnAppProvider fnError={props.fnError}>
+    <AppWrapper app={app} isMFE>
       <DashboardPortal {...props} />
-    </FnAppProvider>
+    </AppWrapper>
   );
 };
 
@@ -54,6 +56,8 @@ export const DashboardPortal: FC<FNDashboardComponentProps> = (p) => {
 
   return (
     <RenderPortal ID="grafana-portal">
+      <ModalRoot />
+      <PortalContainer />
       {content}
     </RenderPortal>
   );
diff --git a/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx b/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
index 05d5206261aa4..d28dcd6c41630 100644
--- a/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
+++ b/public/app/fn-app/fn-dashboard-page/render-fn-dashboard.tsx
@@ -1,5 +1,5 @@
 import { merge, isFunction, isEqual } from 'lodash';
-import { useEffect, FC, useMemo } from 'react';
+import { useEffect, FC, useMemo, useRef } from 'react';
 
 import { locationService as locationSrv, HistoryWrapper } from '@grafana/runtime';
 import DashboardPage, { DashboardPageProps } from 'app/features/dashboard/containers/DashboardPage';
@@ -28,6 +28,7 @@ const DEFAULT_DASHBOARD_PAGE_PROPS: Pick<DashboardPageProps, 'history' | 'route'
 
 export const RenderFNDashboard: FC<FNDashboardProps> = (props) => {
   const { queryParams, controlsContainer, setErrors, hiddenVariables, isLoading } = props;
+  const uidRef = useRef<string | null>(null);
 
   const firstError = useSelector((state: StoreState) => {
     const { appNotifications } = state;
@@ -50,15 +51,22 @@ export const RenderFNDashboard: FC<FNDashboardProps> = (props) => {
   }, [firstError, setErrors]);
 
   useEffect(() => {
-    const searchParams = getSearchParamsObject();
-    if (isEqual(searchParams, queryParams)) {
+    let searchParams = getSearchParamsObject();
+    if (isEqual(searchParams, queryParams) && uidRef.current === props.uid) {
       return;
     }
+    if (uidRef.current !== props.uid) {
+      searchParams = {
+        ...(searchParams.from ? { from: searchParams.from } : {}),
+        ...(searchParams.to ? { to: searchParams.to } : {}),
+      };
+    }
     locationService.fnPathnameChange(window.location.pathname, {
       ...searchParams,
       ...queryParams,
     });
-  }, [queryParams]);
+    uidRef.current = props.uid;
+  }, [queryParams, props.uid]);
 
   const dashboardPageProps: DashboardPageProps = useMemo(() => {
     return merge({}, DEFAULT_DASHBOARD_PAGE_PROPS, {