From 0e9c50933fbd790291004dbcf333d11d645c5aa2 Mon Sep 17 00:00:00 2001
From: Aleksandr Lesnenko <alxnddr@users.noreply.github.com>
Date: Fri, 17 Mar 2023 19:00:40 -0300
Subject: [PATCH] fix how isCustomWidgetCheck detects if widgets are react
 components (#29334)

---
 .../models/models-metadata.cy.spec.js         | 22 +++++++++++++++++++
 frontend/src/metabase-types/guards/forms.ts   |  3 ++-
 frontend/src/metabase-types/guards/react.ts   | 12 ++++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/e2e/test/scenarios/models/models-metadata.cy.spec.js b/e2e/test/scenarios/models/models-metadata.cy.spec.js
index 12779aef0e3..53d94eb2465 100644
--- a/e2e/test/scenarios/models/models-metadata.cy.spec.js
+++ b/e2e/test/scenarios/models/models-metadata.cy.spec.js
@@ -152,6 +152,28 @@ describe("scenarios > models metadata", () => {
     cy.findByText("Pre-tax ($)");
   });
 
+  it("should allow setting column relations (metabase#29318)", () => {
+    cy.createNativeQuestion(
+      {
+        name: "Native Model",
+        dataset: true,
+        native: {
+          query: "SELECT * FROM ORDERS",
+        },
+      },
+      { visitQuestion: true },
+    );
+    openQuestionActions();
+    cy.findByText("Edit metadata").click();
+    openColumnOptions("USER_ID");
+    setColumnType("No special type", "Foreign Key");
+    cy.findByText("Select a target").click();
+    cy.findByText("People → ID").click();
+    cy.button("Save changes").click();
+    // TODO: Not much to do with it at the moment beyond saving it.
+    // Check that the relation is automatically suggested in the notebook once it is implemented.
+  });
+
   it("should keep metadata in sync with the query", () => {
     cy.createNativeQuestion(
       {
diff --git a/frontend/src/metabase-types/guards/forms.ts b/frontend/src/metabase-types/guards/forms.ts
index e1ea0900b0c..99693221940 100644
--- a/frontend/src/metabase-types/guards/forms.ts
+++ b/frontend/src/metabase-types/guards/forms.ts
@@ -3,12 +3,13 @@ import type {
   CustomFormFieldDefinition,
   FormFieldDefinition,
 } from "metabase-types/forms";
+import { isReactComponent } from "./react";
 
 export function isCustomWidget(
   formField: FormFieldDefinition,
 ): formField is CustomFormFieldDefinition {
   return (
     !(formField as StandardFormFieldDefinition).type &&
-    typeof (formField as CustomFormFieldDefinition).widget === "function"
+    isReactComponent((formField as CustomFormFieldDefinition).widget)
   );
 }
diff --git a/frontend/src/metabase-types/guards/react.ts b/frontend/src/metabase-types/guards/react.ts
index 0fa5ae58f48..d31d9170155 100644
--- a/frontend/src/metabase-types/guards/react.ts
+++ b/frontend/src/metabase-types/guards/react.ts
@@ -8,3 +8,15 @@ export function isReactDOMTypeElement(
 ): element is React.ReactElement {
   return ReactIs.isElement(element) && typeof element.type === "string";
 }
+
+export function isReactComponent(
+  component: any,
+): component is React.FC | React.Component | React.ExoticComponent {
+  return (
+    typeof component === "function" ||
+    // Checking for "Exotic" components such as ones returned by memo, forwardRef
+    (typeof component === "object" &&
+      "$$typeof" in component &&
+      typeof component["$$typeof"] === "symbol")
+  );
+}
-- 
GitLab