From 5e3da93ebcc71d256ef2a25474e115c4c72883b5 Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Mon, 12 Sep 2022 22:13:47 +0300
Subject: [PATCH] Make it clearer how to connect a SQL question to a dashboard
 filter (#25324)

---
 .../DashCardCardParameterMapper.jsx           | 24 +++++++++++++--
 .../DashCardCardParameterMapper.styled.jsx    | 29 ++++++++++++++++++-
 frontend/src/metabase/dashboard/utils.js      | 23 +++++++++++++++
 .../parameters/utils/parameter-type.ts        |  5 ++++
 .../dashboard-filters/parameters.cy.spec.js   |  6 ++--
 5 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.jsx b/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.jsx
index 5457fc15816..95b81bc6ebf 100644
--- a/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.jsx
+++ b/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.jsx
@@ -4,6 +4,7 @@ import { connect } from "react-redux";
 import _ from "underscore";
 import { t } from "ttag";
 
+import MetabaseSettings from "metabase/lib/settings";
 import Icon from "metabase/components/Icon";
 import Tooltip from "metabase/components/Tooltip";
 import TippyPopover from "metabase/components/Popover/TippyPopover";
@@ -12,6 +13,8 @@ import { isVariableTarget } from "metabase/parameters/utils/targets";
 import { isDateParameter } from "metabase/parameters/utils/parameter-type";
 import { getMetadata } from "metabase/selectors/metadata";
 import {
+  getNativeDashCardEmptyMappingText,
+  isNativeDashCard,
   isVirtualDashCard,
   showVirtualDashCardInfoText,
 } from "metabase/dashboard/utils";
@@ -34,6 +37,10 @@ import {
   ChevrondownIcon,
   KeyIcon,
   Warning,
+  NativeCardDefault,
+  NativeCardIcon,
+  NativeCardText,
+  NativeCardLink,
 } from "./DashCardCardParameterMapper.styled";
 
 function formatSelected({ name, sectionName }) {
@@ -94,6 +101,7 @@ function DashCardCardParameterMapper({
   );
 
   const isVirtual = isVirtualDashCard(dashcard);
+  const isNative = isNativeDashCard(dashcard);
 
   const hasPermissionsToMap = useMemo(() => {
     if (isVirtual) {
@@ -155,14 +163,14 @@ function DashCardCardParameterMapper({
     ]);
 
   const headerContent = useMemo(() => {
-    if (!isVirtual) {
+    if (!isVirtual && !(isNative && isDisabled)) {
       return t`Column to filter on`;
     } else if (dashcard.size_y !== 1 || isMobile) {
       return t`Variable to map to`;
     } else {
       return null;
     }
-  }, [dashcard, isVirtual, isMobile]);
+  }, [dashcard, isVirtual, isNative, isDisabled, isMobile]);
 
   const mappingInfoText = t`You can connect widgets to {{variables}} in text cards.`;
 
@@ -185,6 +193,18 @@ function DashCardCardParameterMapper({
             />
           </TextCardDefault>
         )
+      ) : isNative && isDisabled ? (
+        <NativeCardDefault>
+          <NativeCardIcon name="info" />
+          <NativeCardText>
+            {getNativeDashCardEmptyMappingText(editingParameter)}
+          </NativeCardText>
+          <NativeCardLink
+            href={MetabaseSettings.docsUrl(
+              "questions/native-editor/sql-parameters",
+            )}
+          >{t`Learn how`}</NativeCardLink>
+        </NativeCardDefault>
       ) : (
         <>
           {headerContent && <Header>{headerContent}</Header>}
diff --git a/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.styled.jsx b/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.styled.jsx
index f5e7b1fb801..c3ce7ba1d07 100644
--- a/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.styled.jsx
+++ b/frontend/src/metabase/dashboard/components/DashCardCardParameterMapper.styled.jsx
@@ -5,6 +5,7 @@ import { space } from "metabase/styled-components/theme";
 import { color, lighten } from "metabase/lib/colors";
 import Icon from "metabase/components/Icon";
 import Button from "metabase/core/components/Button";
+import ExternalLink from "metabase/core/components/ExternalLink";
 
 export const Container = styled.div`
   margin: ${space(1)} 0;
@@ -14,7 +15,7 @@ export const Container = styled.div`
 `;
 
 export const TextCardDefault = styled.div`
-  color: ${color("text-medium")};
+  color: ${color("text-dark")};
   margin: ${space(1)} 0;
   display: flex;
   flex-direction: row;
@@ -22,6 +23,32 @@ export const TextCardDefault = styled.div`
   line-height: 1.5rem;
 `;
 
+export const NativeCardDefault = styled.div`
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+`;
+
+export const NativeCardIcon = styled(Icon)`
+  color: ${color("text-medium")};
+  margin-bottom: 0.5rem;
+  width: 1.25rem;
+  height: 1.25rem;
+`;
+
+export const NativeCardText = styled.div`
+  color: ${color("text-dark")};
+  max-width: 15rem;
+  text-align: center;
+  line-height: 1.5rem;
+`;
+
+export const NativeCardLink = styled(ExternalLink)`
+  color: ${color("brand")};
+  font-weight: bold;
+  margin-top: 0.5rem;
+`;
+
 export const CardLabel = styled.div`
   font-size: 0.83em;
   margin-bottom: ${space(1)};
diff --git a/frontend/src/metabase/dashboard/utils.js b/frontend/src/metabase/dashboard/utils.js
index a4b23ad6219..bb59522c603 100644
--- a/frontend/src/metabase/dashboard/utils.js
+++ b/frontend/src/metabase/dashboard/utils.js
@@ -1,5 +1,12 @@
 import _ from "underscore";
 import Utils from "metabase/lib/utils";
+import { isNative } from "metabase/lib/query";
+import { t } from "ttag";
+import {
+  isDateParameter,
+  isNumberParameter,
+  isStringParameter,
+} from "metabase/parameters/utils/parameter-type";
 
 export function syncParametersAndEmbeddingParams(before, after) {
   if (after.parameters && before.embedding_params) {
@@ -51,6 +58,10 @@ export function isVirtualDashCard(dashcard) {
   return _.isObject(dashcard.visualization_settings.virtual_card);
 }
 
+export function isNativeDashCard(dashcard) {
+  return isNative(dashcard.card?.dataset_query);
+}
+
 // For a virtual (text) dashcard without any parameters, returns a boolean indicating whether we should display the
 // info text about parameter mapping in the card itself or as a tooltip.
 export function showVirtualDashCardInfoText(dashcard, isMobile) {
@@ -61,6 +72,18 @@ export function showVirtualDashCardInfoText(dashcard, isMobile) {
   }
 }
 
+export function getNativeDashCardEmptyMappingText(parameter) {
+  if (isDateParameter(parameter)) {
+    return t`Add a date variable to this question to connect it to a dashboard filter.`;
+  } else if (isNumberParameter(parameter)) {
+    return t`Add a number variable to this question to connect it to a dashboard filter.`;
+  } else if (isStringParameter(parameter)) {
+    return t`Add a string variable to this question to connect it to a dashboard filter.`;
+  } else {
+    return t`Add a variable to this question to connect it to a dashboard filter.`;
+  }
+}
+
 export function getAllDashboardCards(dashboard) {
   const results = [];
   if (dashboard) {
diff --git a/frontend/src/metabase/parameters/utils/parameter-type.ts b/frontend/src/metabase/parameters/utils/parameter-type.ts
index 5100ee88129..372f754505b 100644
--- a/frontend/src/metabase/parameters/utils/parameter-type.ts
+++ b/frontend/src/metabase/parameters/utils/parameter-type.ts
@@ -33,6 +33,11 @@ export function isNumberParameter(parameter: Parameter) {
   return type === "number";
 }
 
+export function isStringParameter(parameter: Parameter) {
+  const type = getParameterType(parameter);
+  return type === "string";
+}
+
 export function isFieldFilterParameter(
   parameter: Parameter,
 ): parameter is FieldFilterUiParameter {
diff --git a/frontend/test/metabase/scenarios/dashboard-filters/parameters.cy.spec.js b/frontend/test/metabase/scenarios/dashboard-filters/parameters.cy.spec.js
index 272ef2a8873..b9ea2cf2df2 100644
--- a/frontend/test/metabase/scenarios/dashboard-filters/parameters.cy.spec.js
+++ b/frontend/test/metabase/scenarios/dashboard-filters/parameters.cy.spec.js
@@ -271,7 +271,7 @@ describe("scenarios > dashboard > parameters", () => {
       cy.icon("pencil").click();
       cy.icon("filter").click();
       cy.findByText("ID").click();
-      cy.findByText("No valid fields");
+      cy.findByText(/Add a variable to this question/).should("be.visible");
 
       // Confirm that the correct parameter type is connected to the native question's field filter
       cy.findByText(matchingFilterType.name).find(".Icon-gear").click();
@@ -301,7 +301,9 @@ describe("scenarios > dashboard > parameters", () => {
       // Confirm that it is not possible to connect filter to the updated question anymore (metabase#9299)
       cy.icon("pencil").click();
       cy.findByText(matchingFilterType.name).find(".Icon-gear").click();
-      cy.findByText("No valid fields");
+      cy.findByText(/Add a string variable to this question/).should(
+        "be.visible",
+      );
     });
   });
 
-- 
GitLab