From e07e28ee1f807e9efd5cbed3f855fd83553799ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=B2=20Pretto?= <info@npretto.com>
Date: Fri, 13 Sep 2024 19:16:20 +0200
Subject: [PATCH] first iteration to scope sdk styles to our components
 (#47764)

* first iteration to scope sdk styles to our components

* basic css reset for buttons

* clean up duplicated css

* make sdk components use instance font if no font is passed

* cleanin up and remove defaultProps not needed

* fix tests

* adds EMBEDDING_SDK_PORTAL_CONTAINER_ELEMENT_ID

* Update enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx

* Update enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>

* fix import

* prettier --write

* we don't need theme.fontFamily, added a comment

* rename EMBEDDING_SDK_PORTAL_CONTAINER_ELEMENT_ID to EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID

* fix unit test

---------

Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev>
---
 .../private/AppInitializeController.tsx       | 36 -----------
 .../private/PublicComponentStylesWrapper.tsx  | 23 +++++++
 .../private/SdkGlobalFontsStyles.tsx          | 36 +++++++++++
 .../private/SdkGlobalStylesWrapper.tsx        | 54 ----------------
 .../SdkGlobalStylesWrapper.unit.spec.tsx      | 61 -------------------
 .../components/private/SdkThemeProvider.tsx   |  9 +--
 .../components/public/MetabaseProvider.tsx    | 53 +++++++++++-----
 .../frontend/src/embedding-sdk/config.ts      |  1 +
 .../lib/theme/default-component-theme.ts      | 29 ++++++++-
 .../lib/theme/get-embedding-theme.ts          | 10 +--
 .../theme/get-embedding-theme.unit.spec.ts    | 21 ++++---
 .../components/Popover/TippyPopover.tsx       |  5 +-
 .../core/components/Tooltip/Tooltip.tsx       |  5 +-
 .../TableInteractive/TableInteractive.jsx     |  4 +-
 frontend/test/__support__/ui.tsx              | 12 ++--
 15 files changed, 161 insertions(+), 198 deletions(-)
 delete mode 100644 enterprise/frontend/src/embedding-sdk/components/private/AppInitializeController.tsx
 create mode 100644 enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalFontsStyles.tsx
 delete mode 100644 enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.tsx
 delete mode 100644 enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.unit.spec.tsx

diff --git a/enterprise/frontend/src/embedding-sdk/components/private/AppInitializeController.tsx b/enterprise/frontend/src/embedding-sdk/components/private/AppInitializeController.tsx
deleted file mode 100644
index 35b881f8ae9..00000000000
--- a/enterprise/frontend/src/embedding-sdk/components/private/AppInitializeController.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { ReactNode } from "react";
-import { t } from "ttag";
-
-import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
-import { useInitData } from "embedding-sdk/hooks";
-import { useSdkSelector } from "embedding-sdk/store";
-import { getIsInitialized } from "embedding-sdk/store/selectors";
-import type { SDKConfig } from "embedding-sdk/types";
-
-import { SdkGlobalStylesWrapper } from "./SdkGlobalStylesWrapper";
-
-interface AppInitializeControllerProps {
-  children: ReactNode;
-  config: SDKConfig;
-  className?: string;
-}
-
-export const AppInitializeController = ({
-  config,
-  children,
-  className,
-}: AppInitializeControllerProps) => {
-  useInitData({ config });
-
-  const isInitialized = useSdkSelector(getIsInitialized);
-
-  return (
-    <SdkGlobalStylesWrapper
-      baseUrl={config.metabaseInstanceUrl}
-      id={EMBEDDING_SDK_ROOT_ELEMENT_ID}
-      className={className}
-    >
-      {!isInitialized ? <div>{t`Loading…`}</div> : children}
-    </SdkGlobalStylesWrapper>
-  );
-};
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/PublicComponentStylesWrapper.tsx b/enterprise/frontend/src/embedding-sdk/components/private/PublicComponentStylesWrapper.tsx
index ed178451515..2cc306f7c1b 100644
--- a/enterprise/frontend/src/embedding-sdk/components/private/PublicComponentStylesWrapper.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/private/PublicComponentStylesWrapper.tsx
@@ -9,15 +9,38 @@ import { saveDomImageStyles } from "metabase/visualizations/lib/save-chart-image
  * even when rendered under a React portal.
  */
 export const PublicComponentStylesWrapper = styled.div`
+  // Try to reset as much as possible to avoid css leaking from host app to our components
+  all: initial;
+  text-decoration: none;
+
+  // # Basic css reset
+  // We can't apply a global css reset as it would leak into the host app
+  // but we can't also apply our entire css reset scoped to this container,
+  // as it would be of higher specificity than some of our styles.
+  // We'll have to hand pick the css resets that we neeed
+
+  button {
+    border: 0;
+    background-color: transparent;
+  }
+  // end of RESET
+
+  font-style: normal;
+
   width: 100%;
   height: 100%;
 
   position: relative;
 
+  font-size: ${({ theme }) => theme.other.fontSize};
+
   font-weight: 400;
   color: var(--mb-color-text-dark);
   font-family: var(--mb-default-font-family), sans-serif;
 
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+
   ${aceEditorStyles}
   ${saveDomImageStyles}
 
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalFontsStyles.tsx b/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalFontsStyles.tsx
new file mode 100644
index 00000000000..3c8f7116173
--- /dev/null
+++ b/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalFontsStyles.tsx
@@ -0,0 +1,36 @@
+import { Global, css } from "@emotion/react";
+import { useMemo } from "react";
+import { useSelector } from "react-redux";
+
+import { defaultFontFiles } from "metabase/css/core/fonts.styled";
+import { getFontFiles } from "metabase/styled-components/selectors";
+
+/**
+ * css style to define the font files for the SDK
+ */
+export const SdkFontsGlobalStyles = ({ baseUrl }: { baseUrl: string }) => {
+  const fontFiles = useSelector(getFontFiles);
+
+  const fontStyles = useMemo(
+    () => css`
+      // built in fonts
+      ${defaultFontFiles({ baseUrl })}
+
+      // custom fonts
+      ${fontFiles?.map(
+        file => css`
+          @font-face {
+            font-family: "Custom";
+            src: url(${encodeURI(file.src)}) format("${file.fontFormat}");
+            font-weight: ${file.fontWeight};
+            font-style: normal;
+            font-display: swap;
+          }
+        `,
+      )}
+    `,
+    [fontFiles, baseUrl],
+  );
+
+  return <Global styles={fontStyles} />;
+};
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.tsx b/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.tsx
deleted file mode 100644
index 759de05816e..00000000000
--- a/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { css } from "@emotion/react";
-import styled from "@emotion/styled";
-import type { HTMLAttributes } from "react";
-
-import { rootStyle } from "metabase/css/core/base.styled";
-import { defaultFontFiles } from "metabase/css/core/fonts.styled";
-import { useSelector } from "metabase/lib/redux";
-import { getFontFiles } from "metabase/styled-components/selectors";
-import type { FontFile } from "metabase-types/api";
-
-interface SdkContentWrapperProps {
-  baseUrl?: string;
-}
-
-export function SdkGlobalStylesWrapper({
-  baseUrl,
-  ...divProps
-}: SdkContentWrapperProps & HTMLAttributes<HTMLDivElement>) {
-  const fontFiles = useSelector(getFontFiles);
-  return (
-    <>
-      <SdkGlobalStylesInner
-        baseUrl={baseUrl}
-        fontFiles={fontFiles}
-        {...divProps}
-      />
-    </>
-  );
-}
-
-const SdkGlobalStylesInner = styled.div<
-  SdkContentWrapperProps & {
-    fontFiles: FontFile[] | null;
-  }
->`
-  font-size: ${({ theme }) => theme.other.fontSize};
-
-  ${rootStyle}
-
-  ${({ baseUrl }) => defaultFontFiles({ baseUrl })}
-
-  ${({ fontFiles }) =>
-    fontFiles?.map(
-      file => css`
-        @font-face {
-          font-family: "Custom";
-          src: url(${encodeURI(file.src)}) format("${file.fontFormat}");
-          font-weight: ${file.fontWeight};
-          font-style: normal;
-          font-display: swap;
-        }
-      `,
-    )}
-`;
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.unit.spec.tsx b/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.unit.spec.tsx
deleted file mode 100644
index 831b9e20363..00000000000
--- a/enterprise/frontend/src/embedding-sdk/components/private/SdkGlobalStylesWrapper.unit.spec.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { renderWithProviders, screen } from "__support__/ui";
-import { SdkGlobalStylesWrapper } from "embedding-sdk/components/private/SdkGlobalStylesWrapper";
-import { SdkThemeProvider } from "embedding-sdk/components/private/SdkThemeProvider";
-import { Text } from "metabase/ui";
-import {
-  createMockSettingsState,
-  createMockState,
-} from "metabase-types/store/mocks";
-
-describe("SdkGlobalStylesWrapper", () => {
-  it("injects the font-face declaration when available", () => {
-    const state = createMockState({
-      settings: createMockSettingsState({
-        "application-font-files": [
-          {
-            src: "https://example.com/foo.woff2",
-            fontFormat: "woff2",
-            fontWeight: 700,
-          },
-        ],
-      }),
-    });
-
-    renderWithProviders(<SdkGlobalStylesWrapper />, {
-      storeInitialState: state,
-    });
-
-    const rules = Array.from(document.styleSheets).flatMap(sheet =>
-      Array.from(sheet.cssRules || []),
-    );
-
-    const fontFaceRule = rules.find(
-      rule =>
-        rule.constructor.name === "CSSFontFaceRule" &&
-        rule.cssText.includes("foo.woff2"),
-    )!;
-
-    expect(fontFaceRule).toBeDefined();
-    expect(fontFaceRule.cssText).toContain("font-weight: 700");
-  });
-
-  // TODO: Add substitute tests since we can't test CSS custom properties with JSDom
-  // eslint-disable-next-line jest/no-disabled-tests
-  it.skip("should use foreground color from the theme", () => {
-    const theme = {
-      colors: { "text-primary": "rgb(255, 0, 255)" },
-    };
-
-    renderWithProviders(
-      <SdkThemeProvider theme={theme}>
-        <SdkGlobalStylesWrapper>
-          <Text>Hello world</Text>
-        </SdkGlobalStylesWrapper>
-      </SdkThemeProvider>,
-    );
-
-    expect(window.getComputedStyle(screen.getByText("Hello world")).color).toBe(
-      "rgb(255, 0, 255)",
-    );
-  });
-});
diff --git a/enterprise/frontend/src/embedding-sdk/components/private/SdkThemeProvider.tsx b/enterprise/frontend/src/embedding-sdk/components/private/SdkThemeProvider.tsx
index c6932e6e609..3e6c9d78b19 100644
--- a/enterprise/frontend/src/embedding-sdk/components/private/SdkThemeProvider.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/private/SdkThemeProvider.tsx
@@ -19,6 +19,7 @@ interface Props {
 }
 
 export const SdkThemeProvider = ({ theme, children }: Props) => {
+  const font = useSelector(getFont);
   const appColors = useSelector(state =>
     getApplicationColors(getSettings(state)),
   );
@@ -28,18 +29,18 @@ export const SdkThemeProvider = ({ theme, children }: Props) => {
     // This must be done before ThemeProvider calls getThemeOverrides.
     setGlobalEmbeddingColors(theme?.colors, appColors);
 
-    return theme && getEmbeddingThemeOverride(theme);
-  }, [appColors, theme]);
+    return theme && getEmbeddingThemeOverride(theme, font);
+  }, [appColors, theme, font]);
 
   return (
     <ThemeProvider theme={themeOverride}>
-      <SDKGlobalStyles />
+      <GlobalSdkCssVariables />
       {children}
     </ThemeProvider>
   );
 };
 
-function SDKGlobalStyles() {
+function GlobalSdkCssVariables() {
   const theme = useMantineTheme();
   const font = useSelector(getFont);
 
diff --git a/enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx b/enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx
index 8c7a3a58fcb..77843312798 100644
--- a/enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx
+++ b/enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx
@@ -1,11 +1,13 @@
 import type { Action, Store } from "@reduxjs/toolkit";
-import { type JSX, type ReactNode, useEffect } from "react";
-import { memo } from "react";
+import { type JSX, type ReactNode, memo, useEffect } from "react";
 import { Provider } from "react-redux";
 
-import { AppInitializeController } from "embedding-sdk/components/private/AppInitializeController";
 import { SdkThemeProvider } from "embedding-sdk/components/private/SdkThemeProvider";
-import { DEFAULT_FONT } from "embedding-sdk/config";
+import {
+  EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID,
+  EMBEDDING_SDK_ROOT_ELEMENT_ID,
+} from "embedding-sdk/config";
+import { useInitData } from "embedding-sdk/hooks";
 import type { SdkEventHandlersConfig } from "embedding-sdk/lib/events";
 import type { SdkPluginsConfig } from "embedding-sdk/lib/plugins";
 import { store } from "embedding-sdk/store";
@@ -22,8 +24,11 @@ import type { MetabaseTheme } from "embedding-sdk/types/theme";
 import { setOptions } from "metabase/redux/embed";
 import { EmotionCacheProvider } from "metabase/styled-components/components/EmotionCacheProvider";
 
-import "metabase/css/vendor.css";
+import { PublicComponentWrapper } from "../private/PublicComponentWrapper";
+import { SdkFontsGlobalStyles } from "../private/SdkGlobalFontsStyles";
+
 import "metabase/css/index.module.css";
+import "metabase/css/vendor.css";
 
 export interface MetabaseProviderProps {
   children: ReactNode;
@@ -47,7 +52,8 @@ export const MetabaseProviderInternal = ({
   store,
   className,
 }: InternalMetabaseProviderProps): JSX.Element => {
-  const { fontFamily = DEFAULT_FONT } = theme ?? {};
+  const { fontFamily } = theme ?? {};
+  useInitData({ config });
 
   useEffect(() => {
     if (fontFamily) {
@@ -76,20 +82,35 @@ export const MetabaseProviderInternal = ({
   }, [store, config.metabaseInstanceUrl]);
 
   return (
-    <Provider store={store}>
-      <EmotionCacheProvider>
-        <SdkThemeProvider theme={theme}>
-          <AppInitializeController className={className} config={config}>
-            {children}
-          </AppInitializeController>
-        </SdkThemeProvider>
-      </EmotionCacheProvider>
-    </Provider>
+    <EmotionCacheProvider>
+      <SdkThemeProvider theme={theme}>
+        <SdkFontsGlobalStyles baseUrl={config.metabaseInstanceUrl} />
+        <div className={className} id={EMBEDDING_SDK_ROOT_ELEMENT_ID}>
+          <PortalContainer />
+          {children}
+        </div>
+      </SdkThemeProvider>
+    </EmotionCacheProvider>
   );
 };
 
 export const MetabaseProvider = memo(function MetabaseProvider(
   props: MetabaseProviderProps,
 ) {
-  return <MetabaseProviderInternal store={store} {...props} />;
+  return (
+    <Provider store={store}>
+      <MetabaseProviderInternal store={store} {...props} />
+    </Provider>
+  );
 });
+
+/**
+ * This is the portal container used by popovers modals etc, it is wrapped with withPublicComponentWrapper
+ * so that it has our styles applied.
+ * Mantine components needs to have the defaultProps set to use `EMBEDDING_SDK_PORTAL_CONTAINER_ELEMENT_ID` as target for the portal
+ */
+const PortalContainer = () => (
+  <PublicComponentWrapper>
+    <div id={EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID}></div>
+  </PublicComponentWrapper>
+);
diff --git a/enterprise/frontend/src/embedding-sdk/config.ts b/enterprise/frontend/src/embedding-sdk/config.ts
index a80fe74f656..1606d3a8401 100644
--- a/enterprise/frontend/src/embedding-sdk/config.ts
+++ b/enterprise/frontend/src/embedding-sdk/config.ts
@@ -1,5 +1,6 @@
 export const DEFAULT_FONT = "Lato";
 export const EMBEDDING_SDK_ROOT_ELEMENT_ID = "metabase-sdk-root";
+export const EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID = "metabase-sdk-portal-root";
 
 export const getEmbeddingSdkVersion = () =>
   process.env.EMBEDDING_SDK_VERSION ?? "unknown";
diff --git a/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts b/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts
index 08183c465b3..eed035bc490 100644
--- a/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts
+++ b/enterprise/frontend/src/embedding-sdk/lib/theme/default-component-theme.ts
@@ -1,7 +1,7 @@
 import { merge } from "icepick";
 
 import type { MetabaseComponentTheme } from "embedding-sdk";
-import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
+import { EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID } from "embedding-sdk/config";
 import type { DeepPartial } from "embedding-sdk/types/utils";
 import type { MantineThemeOverride } from "metabase/ui";
 
@@ -124,6 +124,11 @@ export const DEFAULT_EMBEDDED_COMPONENT_THEME: MetabaseComponentTheme = merge<
   },
 });
 
+// What's up with the commented `satisfies`?
+// Mantine docs says they don't typecheck default props because of performance reasons.
+// To be sure to not slow down typescript I left the check commented.
+// If you change any of the default props please verify that the types are correct
+
 export function getEmbeddingComponentOverrides(
   theme?: DeepPartial<MetabaseComponentTheme>,
 ): MantineThemeOverride["components"] {
@@ -132,11 +137,31 @@ export function getEmbeddingComponentOverrides(
       defaultProps: {
         withinPortal: true,
         portalProps: {
-          target: `#${EMBEDDING_SDK_ROOT_ELEMENT_ID}`,
+          target: `#${EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID}`,
         },
 
         ...(theme?.popover?.zIndex && { zIndex: theme.popover.zIndex }),
       },
     },
+    ModalRoot: {
+      defaultProps: {
+        withinPortal: true,
+        target: `#${EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID}`,
+      }, //  satisfies Partial<ModalRootProps>,
+    },
+    Modal: {
+      defaultProps: {
+        withinPortal: true,
+        target: `#${EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID}`,
+      }, // satisfies Partial<ModalProps>,
+    },
+    Popover: {
+      defaultProps: {
+        withinPortal: true,
+        portalProps: {
+          target: `#${EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID}`,
+        },
+      }, // satisfies Partial<PopoverProps>,
+    },
   };
 }
diff --git a/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts b/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts
index 63f2549df8e..38fdad6f6a8 100644
--- a/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts
+++ b/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.ts
@@ -18,10 +18,7 @@ import {
 import type { MappableSdkColor } from "./embedding-color-palette";
 import { SDK_TO_MAIN_APP_COLORS_MAPPING } from "./embedding-color-palette";
 
-const getFontFamily = (theme: MetabaseTheme) =>
-  theme.fontFamily ?? DEFAULT_FONT;
-
-const SDK_BASE_FONT_SIZE = `${DEFAULT_SDK_FONT_SIZE / 16}em`;
+const SDK_BASE_FONT_SIZE = `${DEFAULT_SDK_FONT_SIZE}px`;
 
 /**
  * Transforms a public-facing Metabase theme configuration
@@ -29,6 +26,7 @@ const SDK_BASE_FONT_SIZE = `${DEFAULT_SDK_FONT_SIZE / 16}em`;
  */
 export function getEmbeddingThemeOverride(
   theme: MetabaseTheme,
+  font: string | undefined,
 ): MantineThemeOverride {
   const components: MetabaseComponentTheme = merge(
     DEFAULT_EMBEDDED_COMPONENT_THEME,
@@ -36,7 +34,9 @@ export function getEmbeddingThemeOverride(
   );
 
   const override: MantineThemeOverride = {
-    fontFamily: getFontFamily(theme),
+    // font is coming from either redux, where we store theme.fontFamily,
+    // or from the instance settings, we're adding a default to be used while loading the settings
+    fontFamily: font ?? DEFAULT_FONT,
 
     ...(theme.lineHeight && { lineHeight: theme.lineHeight }),
 
diff --git a/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.unit.spec.ts b/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.unit.spec.ts
index defd9d3755e..99dd7361798 100644
--- a/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.unit.spec.ts
+++ b/enterprise/frontend/src/embedding-sdk/lib/theme/get-embedding-theme.unit.spec.ts
@@ -7,16 +7,19 @@ import { getEmbeddingThemeOverride } from "./get-embedding-theme";
 
 describe("Transform Embedding Theme Override", () => {
   it("should transform MetabaseTheme to EmbeddingThemeOverride", () => {
-    const theme = getEmbeddingThemeOverride({
-      lineHeight: 1.5,
-      fontSize: "2rem",
-      fontFamily: "Roboto",
-      colors: {
-        brand: "hotpink",
-        "text-primary": "yellow",
-        "text-tertiary": "green",
+    const theme = getEmbeddingThemeOverride(
+      {
+        lineHeight: 1.5,
+        fontSize: "2rem",
+        fontFamily: "Roboto",
+        colors: {
+          brand: "hotpink",
+          "text-primary": "yellow",
+          "text-tertiary": "green",
+        },
       },
-    });
+      "Roboto",
+    );
 
     expect(theme).toEqual({
       lineHeight: 1.5,
diff --git a/frontend/src/metabase/components/Popover/TippyPopover.tsx b/frontend/src/metabase/components/Popover/TippyPopover.tsx
index d145ba758fc..6185847d725 100644
--- a/frontend/src/metabase/components/Popover/TippyPopover.tsx
+++ b/frontend/src/metabase/components/Popover/TippyPopover.tsx
@@ -4,7 +4,7 @@ import { merge } from "icepick";
 import { useCallback, useMemo, useState } from "react";
 import type * as tippy from "tippy.js";
 
-import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
+import { EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID } from "embedding-sdk/config";
 import EventSandbox from "metabase/components/EventSandbox";
 import { DEFAULT_Z_INDEX } from "metabase/components/Popover/constants";
 import { isCypressActive } from "metabase/env";
@@ -31,7 +31,8 @@ const OFFSET: [number, number] = [0, 5];
 
 function appendTo() {
   return (
-    document.getElementById(EMBEDDING_SDK_ROOT_ELEMENT_ID) || document.body
+    document.getElementById(EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID) ||
+    document.body
   );
 }
 
diff --git a/frontend/src/metabase/core/components/Tooltip/Tooltip.tsx b/frontend/src/metabase/core/components/Tooltip/Tooltip.tsx
index 20791699438..588d01f3ebe 100644
--- a/frontend/src/metabase/core/components/Tooltip/Tooltip.tsx
+++ b/frontend/src/metabase/core/components/Tooltip/Tooltip.tsx
@@ -3,7 +3,7 @@ import { useMemo } from "react";
 import * as React from "react";
 import * as ReactIs from "react-is";
 
-import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
+import { EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID } from "embedding-sdk/config";
 import { DEFAULT_Z_INDEX } from "metabase/components/Popover/constants";
 import { isReducedMotionPreferred } from "metabase/lib/dom";
 import { isReactDOMTypeElement } from "metabase-types/guards";
@@ -49,7 +49,8 @@ function getTargetProps(
 
 function appendTo() {
   return (
-    document.getElementById(EMBEDDING_SDK_ROOT_ELEMENT_ID) || document.body
+    document.getElementById(EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID) ||
+    document.body
   );
 }
 
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
index 5c42e2d4274..b090fb53d9f 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
+++ b/frontend/src/metabase/visualizations/components/TableInteractive/TableInteractive.jsx
@@ -8,7 +8,7 @@ import { Grid, ScrollSync } from "react-virtualized";
 import { t } from "ttag";
 import _ from "underscore";
 
-import { EMBEDDING_SDK_ROOT_ELEMENT_ID } from "embedding-sdk/config";
+import { EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID } from "embedding-sdk/config";
 import ExplicitSize from "metabase/components/ExplicitSize";
 import { QueryColumnInfoPopover } from "metabase/components/MetadataInfo/ColumnInfoPopover";
 import Button from "metabase/core/components/Button";
@@ -180,7 +180,7 @@ class TableInteractive extends Component {
 
     if (this.props.isEmbeddingSdk) {
       const rootElement = document.getElementById(
-        EMBEDDING_SDK_ROOT_ELEMENT_ID,
+        EMBEDDING_SDK_PORTAL_ROOT_ELEMENT_ID,
       );
 
       if (rootElement) {
diff --git a/frontend/test/__support__/ui.tsx b/frontend/test/__support__/ui.tsx
index 197ee3947e3..bb70c3c2070 100644
--- a/frontend/test/__support__/ui.tsx
+++ b/frontend/test/__support__/ui.tsx
@@ -130,11 +130,13 @@ export function renderWithProviders(
   const wrapper = (props: any) => {
     if (mode === "sdk") {
       return (
-        <MetabaseProviderInternal
-          {...props}
-          {...sdkProviderProps}
-          store={store}
-        />
+        <Provider store={store}>
+          <MetabaseProviderInternal
+            {...props}
+            {...sdkProviderProps}
+            store={store}
+          />
+        </Provider>
       );
     }
 
-- 
GitLab