From 9cd7ee902411cbaf11f913c323436399bd83360b Mon Sep 17 00:00:00 2001
From: "Mahatthana (Kelvin) Nomsawadi" <mahatthana.n@gmail.com>
Date: Mon, 27 Jun 2022 09:30:37 +0700
Subject: [PATCH] Offer transparent background in embedding (#23482)

* Offer transparent background in embedding

* Remove nighttime mode toggle for transparent embedded dashboard

* Refactor hasNightModeToggle

* Optimize checkerboard file size
---
 .../dashboard/components/DashboardActions.jsx   |  3 ++-
 .../dashboard/hoc/DashboardControls.jsx         | 17 ++++++++++++-----
 .../metabase/public/components/EmbedFrame.css   | 13 +++++++++++++
 .../components/widgets/AdvancedEmbedPane.jsx    | 10 +++++++---
 .../components/widgets/DisplayOptionsPane.jsx   |  1 +
 .../public/components/widgets/PreviewPane.jsx   |  8 +++++---
 .../components/widgets/PreviewPane.styled.js    |  7 +++++++
 .../app/img/pattern_checkerboard.svg            | 11 +++++++++++
 8 files changed, 58 insertions(+), 12 deletions(-)
 create mode 100644 frontend/src/metabase/public/components/widgets/PreviewPane.styled.js
 create mode 100644 resources/frontend_client/app/img/pattern_checkerboard.svg

diff --git a/frontend/src/metabase/dashboard/components/DashboardActions.jsx b/frontend/src/metabase/dashboard/components/DashboardActions.jsx
index 7537ff5bd6c..8fac8b43b0f 100644
--- a/frontend/src/metabase/dashboard/components/DashboardActions.jsx
+++ b/frontend/src/metabase/dashboard/components/DashboardActions.jsx
@@ -30,6 +30,7 @@ export const getDashboardActions = (
     onRefreshPeriodChange,
     onSharingClick,
     onFullscreenChange,
+    hasNightModeToggle,
   },
 ) => {
   const isPublicLinksEnabled = MetabaseSettings.get("enable-public-sharing");
@@ -117,7 +118,7 @@ export const getDashboardActions = (
     );
   }
 
-  if (!isEditing && isFullscreen) {
+  if (!isEditing && isFullscreen && hasNightModeToggle) {
     buttons.push(
       <Tooltip
         key="night"
diff --git a/frontend/src/metabase/dashboard/hoc/DashboardControls.jsx b/frontend/src/metabase/dashboard/hoc/DashboardControls.jsx
index c73063fee13..44ff13f4305 100644
--- a/frontend/src/metabase/dashboard/hoc/DashboardControls.jsx
+++ b/frontend/src/metabase/dashboard/hoc/DashboardControls.jsx
@@ -24,7 +24,7 @@ export default ComposedComponent =>
 
       state = {
         isFullscreen: false,
-        isNightMode: false,
+        theme: null,
 
         refreshPeriod: null,
 
@@ -66,7 +66,7 @@ export default ComposedComponent =>
             ? null
             : options.refresh,
         );
-        this.setNightMode(options.theme === "night" || options.night); // DEPRECATED: options.night
+        this.setTheme(options.theme);
         this.setFullscreen(options.fullscreen);
         this.setHideParameters(options.hide_parameters);
       };
@@ -84,7 +84,7 @@ export default ComposedComponent =>
         };
         setValue("refresh", this.state.refreshPeriod);
         setValue("fullscreen", this.state.isFullscreen);
-        setValue("theme", this.state.isNightMode ? "night" : null);
+        setValue("theme", this.state.theme);
 
         delete options.night; // DEPRECATED: options.night
 
@@ -127,9 +127,14 @@ export default ComposedComponent =>
         }
       };
 
+      // Preserve existing behavior, while keeping state in a new `theme` key
       setNightMode = isNightMode => {
-        isNightMode = !!isNightMode;
-        this.setState({ isNightMode });
+        const theme = isNightMode ? "night" : null;
+        this.setState({ theme });
+      };
+
+      setTheme = theme => {
+        this.setState({ theme });
       };
 
       setFullscreen = async (isFullscreen, browserFullscreen = true) => {
@@ -213,6 +218,8 @@ export default ComposedComponent =>
           <ComposedComponent
             {...this.props}
             {...this.state}
+            isNightMode={this.state.theme === "night"}
+            hasNightModeToggle={this.state.theme !== "transparent"}
             setRefreshElapsedHook={this.setRefreshElapsedHook}
             loadDashboardParams={this.loadDashboardParams}
             updateDashboardParams={this.updateDashboardParams}
diff --git a/frontend/src/metabase/public/components/EmbedFrame.css b/frontend/src/metabase/public/components/EmbedFrame.css
index 88e698098e5..aa50d6bb355 100644
--- a/frontend/src/metabase/public/components/EmbedFrame.css
+++ b/frontend/src/metabase/public/components/EmbedFrame.css
@@ -38,3 +38,16 @@
 .Theme--night.EmbedFrame .enable-dots .dc-tooltip circle.dot {
   fill: currentColor;
 }
+
+.Theme--transparent.EmbedFrame {
+  background-color: transparent;
+}
+
+.Theme--transparent .EmbedFrame-header,
+.Theme--transparent .EmbedFrame-footer {
+  background-color: transparent;
+}
+
+.Theme--transparent.EmbedFrame .DashCard .Card {
+  background-color: transparent;
+}
diff --git a/frontend/src/metabase/public/components/widgets/AdvancedEmbedPane.jsx b/frontend/src/metabase/public/components/widgets/AdvancedEmbedPane.jsx
index b089bd03241..8af0f2e817c 100644
--- a/frontend/src/metabase/public/components/widgets/AdvancedEmbedPane.jsx
+++ b/frontend/src/metabase/public/components/widgets/AdvancedEmbedPane.jsx
@@ -1,6 +1,8 @@
 /* eslint-disable react/prop-types */
 import React from "react";
 
+import _ from "underscore";
+
 import ToggleLarge from "metabase/components/ToggleLarge";
 import Button from "metabase/core/components/Button";
 import ActionButton from "metabase/components/ActionButton";
@@ -9,8 +11,6 @@ import AdvancedSettingsPane from "./AdvancedSettingsPane";
 import PreviewPane from "./PreviewPane";
 import EmbedCodePane from "./EmbedCodePane";
 
-import _ from "underscore";
-
 const AdvancedEmbedPane = ({
   pane,
   resource,
@@ -74,7 +74,11 @@ const AdvancedEmbedPane = ({
         onChange={() => onChangePane(pane === "preview" ? "code" : "preview")}
       />
       {pane === "preview" ? (
-        <PreviewPane className="flex-full" previewUrl={iframeUrl} />
+        <PreviewPane
+          className="flex-full"
+          previewUrl={iframeUrl}
+          isTransparent={displayOptions.theme === "transparent"}
+        />
       ) : pane === "code" ? (
         <EmbedCodePane
           className="flex-full"
diff --git a/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx b/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx
index 30268dc1ec6..90552622b83 100644
--- a/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx
+++ b/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx
@@ -23,6 +23,7 @@ import {
 const THEME_OPTIONS = [
   { name: t`Light`, value: null },
   { name: t`Dark`, value: "night" },
+  { name: t`Transparent`, value: "transparent" },
 ];
 
 const mapStateToProps = state => ({
diff --git a/frontend/src/metabase/public/components/widgets/PreviewPane.jsx b/frontend/src/metabase/public/components/widgets/PreviewPane.jsx
index c0491026757..8e1ddd8a668 100644
--- a/frontend/src/metabase/public/components/widgets/PreviewPane.jsx
+++ b/frontend/src/metabase/public/components/widgets/PreviewPane.jsx
@@ -2,6 +2,7 @@
 import React, { Component } from "react";
 
 import cx from "classnames";
+import { PreviewPaneContainer } from "./PreviewPane.styled";
 
 export default class PreviewPane extends Component {
   constructor(props) {
@@ -19,9 +20,10 @@ export default class PreviewPane extends Component {
   }
 
   render() {
-    const { className, previewUrl } = this.props;
+    const { className, previewUrl, isTransparent } = this.props;
     return (
-      <div
+      <PreviewPaneContainer
+        isTransparent={isTransparent}
         className={cx(className, "flex relative")}
         style={{ minHeight: 280 }}
       >
@@ -32,7 +34,7 @@ export default class PreviewPane extends Component {
           allowTransparency
           onLoad={() => this.setState({ loading: false })}
         />
-      </div>
+      </PreviewPaneContainer>
     );
   }
 }
diff --git a/frontend/src/metabase/public/components/widgets/PreviewPane.styled.js b/frontend/src/metabase/public/components/widgets/PreviewPane.styled.js
new file mode 100644
index 00000000000..d05de958b05
--- /dev/null
+++ b/frontend/src/metabase/public/components/widgets/PreviewPane.styled.js
@@ -0,0 +1,7 @@
+import styled from "@emotion/styled";
+
+export const PreviewPaneContainer = styled.div`
+  ${({ isTransparent }) =>
+    isTransparent &&
+    `background-image: url("app/img/pattern_checkerboard.svg")`};
+`;
diff --git a/resources/frontend_client/app/img/pattern_checkerboard.svg b/resources/frontend_client/app/img/pattern_checkerboard.svg
new file mode 100644
index 00000000000..4862640de51
--- /dev/null
+++ b/resources/frontend_client/app/img/pattern_checkerboard.svg
@@ -0,0 +1,11 @@
+<svg
+  width="20"
+  height="20"
+  viewBox="0 0 20 20"
+  fill="none"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+>
+  <rect width="10" height="10" fill="#F4F4F4" />
+  <rect x="10" y="10" width="10" height="10" fill="#F4F4F4" />
+</svg>
-- 
GitLab