From 5f3c7c3491954e245d7adb741f8dc8a9d57a7398 Mon Sep 17 00:00:00 2001
From: Benoit Vinay <ben@benoitvinay.com>
Date: Tue, 7 Jun 2022 08:48:35 +0200
Subject: [PATCH] Convert mode to typescript (#22915)

* Mode moved into its own package

* Misc code clena up

* Comment added back in

* modes set to TS

* getMode method WIP

* getMode clean up

* ObjectLiteral type added

* Fix types for Mode

* ObjectLiteral replaced where necessary

* FilterPopover commit issue

Please enter the commit message for your changes. Lines starting

* ObjectLiteral replaced with TS Record

* ObjectLiteral removed
---
 .../src/metabase-lib/lib/{ => Mode}/Mode.ts   | 16 +++---
 .../src/metabase-lib/lib/Mode/constants.ts    | 17 +++++++
 frontend/src/metabase-lib/lib/Mode/index.ts   |  2 +
 frontend/src/metabase-lib/lib/Mode/types.ts   |  3 ++
 .../lib/Mode/utils.ts}                        | 39 +++++++++------
 frontend/src/metabase-types/types/Database.ts |  4 +-
 .../src/metabase-types/types/Visualization.ts |  6 ++-
 frontend/src/metabase-types/types/index.ts    |  6 +--
 frontend/src/metabase/modes/lib/modes.ts      | 49 +++++++++++++++++++
 frontend/src/metabase/static-viz/lib/dates.ts |  2 +-
 .../components/ObjectDetail/ObjectDetail.tsx  |  2 +-
 11 files changed, 110 insertions(+), 36 deletions(-)
 rename frontend/src/metabase-lib/lib/{ => Mode}/Mode.ts (78%)
 create mode 100644 frontend/src/metabase-lib/lib/Mode/constants.ts
 create mode 100644 frontend/src/metabase-lib/lib/Mode/index.ts
 create mode 100644 frontend/src/metabase-lib/lib/Mode/types.ts
 rename frontend/src/{metabase/modes/lib/modes.js => metabase-lib/lib/Mode/utils.ts} (65%)
 create mode 100644 frontend/src/metabase/modes/lib/modes.ts

diff --git a/frontend/src/metabase-lib/lib/Mode.ts b/frontend/src/metabase-lib/lib/Mode/Mode.ts
similarity index 78%
rename from frontend/src/metabase-lib/lib/Mode.ts
rename to frontend/src/metabase-lib/lib/Mode/Mode.ts
index 0bae53dff72..f92ade62aa6 100644
--- a/frontend/src/metabase-lib/lib/Mode.ts
+++ b/frontend/src/metabase-lib/lib/Mode/Mode.ts
@@ -1,5 +1,3 @@
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-nocheck
 import Question from "metabase-lib/lib/Question";
 import { getMode } from "metabase/modes/lib/modes";
 import {
@@ -7,6 +5,7 @@ import {
   ClickObject,
   QueryMode,
 } from "metabase-types/types/Visualization";
+
 export default class Mode {
   _question: Question;
   _queryMode: QueryMode;
@@ -16,15 +15,14 @@ export default class Mode {
     this._queryMode = queryMode;
   }
 
-  static forQuestion(question: Question): Mode | null | undefined {
+  static forQuestion(question: Question): Mode | null {
     // TODO Atte Keinänen 6/22/17: Move getMode here and refactor it after writing tests
     const queryMode = getMode(question);
-
     if (queryMode) {
       return new Mode(question, queryMode);
-    } else {
-      return null;
     }
+
+    return null;
   }
 
   queryMode() {
@@ -36,9 +34,9 @@ export default class Mode {
   }
 
   actionsForClick(
-    clicked: ClickObject | null | undefined,
-    settings,
-    extraData,
+    clicked: ClickObject | undefined,
+    settings: Record<string, any>,
+    extraData: Record<string, any>,
   ): ClickAction[] {
     return this._queryMode.drills().flatMap(actionCreator =>
       actionCreator({
diff --git a/frontend/src/metabase-lib/lib/Mode/constants.ts b/frontend/src/metabase-lib/lib/Mode/constants.ts
new file mode 100644
index 00000000000..7f853f7377e
--- /dev/null
+++ b/frontend/src/metabase-lib/lib/Mode/constants.ts
@@ -0,0 +1,17 @@
+export const MODE_TYPE_DEFAULT = "default";
+export const MODE_TYPE_NATIVE = "native";
+export const MODE_TYPE_SEGMENT = "segment";
+export const MODE_TYPE_METRIC = "metric";
+export const MODE_TYPE_TIMESERIES = "timeseries";
+export const MODE_TYPE_GEO = "geo";
+export const MODE_TYPE_PIVOT = "pivot";
+
+export const MODES_TYPES = [
+  MODE_TYPE_NATIVE,
+  MODE_TYPE_SEGMENT,
+  MODE_TYPE_METRIC,
+  MODE_TYPE_TIMESERIES,
+  MODE_TYPE_GEO,
+  MODE_TYPE_PIVOT,
+  MODE_TYPE_DEFAULT,
+] as const;
diff --git a/frontend/src/metabase-lib/lib/Mode/index.ts b/frontend/src/metabase-lib/lib/Mode/index.ts
new file mode 100644
index 00000000000..e42afab4d3d
--- /dev/null
+++ b/frontend/src/metabase-lib/lib/Mode/index.ts
@@ -0,0 +1,2 @@
+export { default } from "./Mode";
+export { getMode } from "./utils";
diff --git a/frontend/src/metabase-lib/lib/Mode/types.ts b/frontend/src/metabase-lib/lib/Mode/types.ts
new file mode 100644
index 00000000000..6dff0cb96bb
--- /dev/null
+++ b/frontend/src/metabase-lib/lib/Mode/types.ts
@@ -0,0 +1,3 @@
+import { MODES_TYPES } from "./constants";
+
+export type ModeType = typeof MODES_TYPES[number];
diff --git a/frontend/src/metabase/modes/lib/modes.js b/frontend/src/metabase-lib/lib/Mode/utils.ts
similarity index 65%
rename from frontend/src/metabase/modes/lib/modes.js
rename to frontend/src/metabase-lib/lib/Mode/utils.ts
index 73cb7f321cb..5722f0cf983 100644
--- a/frontend/src/metabase/modes/lib/modes.js
+++ b/frontend/src/metabase-lib/lib/Mode/utils.ts
@@ -1,15 +1,18 @@
-import SegmentMode from "../components/modes/SegmentMode";
-import MetricMode from "../components/modes/MetricMode";
-import TimeseriesMode from "../components/modes/TimeseriesMode";
-import GeoMode from "../components/modes/GeoMode";
-import PivotMode from "../components/modes/PivotMode";
-import NativeMode from "../components/modes/NativeMode";
-import DefaultMode from "../components/modes/DefaultMode";
-
+import Question from "metabase-lib/lib/Question";
 import StructuredQuery from "metabase-lib/lib/queries/StructuredQuery";
 import NativeQuery from "metabase-lib/lib/queries/NativeQuery";
+import { ModeType } from "./types";
+import {
+  MODE_TYPE_NATIVE,
+  MODE_TYPE_SEGMENT,
+  MODE_TYPE_METRIC,
+  MODE_TYPE_TIMESERIES,
+  MODE_TYPE_GEO,
+  MODE_TYPE_PIVOT,
+  MODE_TYPE_DEFAULT,
+} from "metabase-lib/lib/Mode/constants";
 
-export function getMode(question) {
+export function getMode(question: Question): ModeType | null {
   if (!question) {
     return null;
   }
@@ -17,7 +20,7 @@ export function getMode(question) {
   const query = question.query();
 
   if (query instanceof NativeQuery) {
-    return NativeMode;
+    return MODE_TYPE_NATIVE;
   }
 
   if (query instanceof StructuredQuery) {
@@ -25,11 +28,13 @@ export function getMode(question) {
     const breakouts = query.breakouts();
 
     if (aggregations.length === 0 && breakouts.length === 0) {
-      return SegmentMode;
+      return MODE_TYPE_SEGMENT;
     }
+
     if (aggregations.length > 0 && breakouts.length === 0) {
-      return MetricMode;
+      return MODE_TYPE_METRIC;
     }
+
     if (aggregations.length > 0 && breakouts.length > 0) {
       const breakoutFields = breakouts.map(b => b.field());
       if (
@@ -38,21 +43,23 @@ export function getMode(question) {
           breakoutFields[0].isDate() &&
           breakoutFields[1].isCategory())
       ) {
-        return TimeseriesMode;
+        return MODE_TYPE_TIMESERIES;
       }
+
       if (breakoutFields.length === 1 && breakoutFields[0].isAddress()) {
-        return GeoMode;
+        return MODE_TYPE_GEO;
       }
+
       if (
         (breakoutFields.length === 1 && breakoutFields[0].isCategory()) ||
         (breakoutFields.length === 2 &&
           breakoutFields[0].isCategory() &&
           breakoutFields[1].isCategory())
       ) {
-        return PivotMode;
+        return MODE_TYPE_PIVOT;
       }
     }
   }
 
-  return DefaultMode;
+  return MODE_TYPE_DEFAULT;
 }
diff --git a/frontend/src/metabase-types/types/Database.ts b/frontend/src/metabase-types/types/Database.ts
index 60cda7f51b2..3ddd49f3aed 100644
--- a/frontend/src/metabase-types/types/Database.ts
+++ b/frontend/src/metabase-types/types/Database.ts
@@ -16,9 +16,7 @@ export type DatabaseFeature =
   | "case-sensitivity-string-filter-options"
   | "binning";
 
-export type DatabaseDetails = {
-  [key: string]: any;
-};
+export type DatabaseDetails = Record<string, any>;
 
 export type DatabaseEngine = string;
 
diff --git a/frontend/src/metabase-types/types/Visualization.ts b/frontend/src/metabase-types/types/Visualization.ts
index de62082172a..257190104b5 100644
--- a/frontend/src/metabase-types/types/Visualization.ts
+++ b/frontend/src/metabase-types/types/Visualization.ts
@@ -35,12 +35,12 @@ export type ClickObject = {
   event?: MouseEvent;
   element?: HTMLElement;
   seriesIndex?: number;
-  settings?: { [key: string]: any };
+  settings?: Record<string, any>;
   origin?: {
     row: Row;
     cols: Column[];
   };
-  extraData?: { [key: string]: any };
+  extraData?: Record<string, any>;
 };
 
 export type ClickAction = {
@@ -59,6 +59,8 @@ export type ClickAction = {
 export type ClickActionProps = {
   question: Question;
   clicked?: ClickObject;
+  settings?: VisualizationSettings;
+  extraData?: Record<string, any>;
 };
 
 export type OnChangeCardAndRun = ({
diff --git a/frontend/src/metabase-types/types/index.ts b/frontend/src/metabase-types/types/index.ts
index c7e847db720..0978d8bba20 100644
--- a/frontend/src/metabase-types/types/index.ts
+++ b/frontend/src/metabase-types/types/index.ts
@@ -15,13 +15,11 @@ export type LocationDescriptor = {
   hash: string;
   pathname: string;
   search?: string;
-  query?: { [key: string]: string };
+  query?: Record<string, any>;
 };
 
 /* Map of query string names to string values */
-export type QueryParams = {
-  [key: string]: string;
-};
+export type QueryParams = Record<string, any>;
 
 /* Metabase API error object returned by the backend */
 export type ApiError = {
diff --git a/frontend/src/metabase/modes/lib/modes.ts b/frontend/src/metabase/modes/lib/modes.ts
new file mode 100644
index 00000000000..1bc3bf784dc
--- /dev/null
+++ b/frontend/src/metabase/modes/lib/modes.ts
@@ -0,0 +1,49 @@
+import SegmentMode from "../components/modes/SegmentMode";
+import MetricMode from "../components/modes/MetricMode";
+import TimeseriesMode from "../components/modes/TimeseriesMode";
+import GeoMode from "../components/modes/GeoMode";
+import PivotMode from "../components/modes/PivotMode";
+import NativeMode from "../components/modes/NativeMode";
+import DefaultMode from "../components/modes/DefaultMode";
+import { QueryMode } from "metabase-types/types/Visualization";
+
+import Question from "metabase-lib/lib/Question";
+import { getMode as getModeFromLib } from "metabase-lib/lib/Mode";
+import {
+  MODE_TYPE_NATIVE,
+  MODE_TYPE_SEGMENT,
+  MODE_TYPE_METRIC,
+  MODE_TYPE_TIMESERIES,
+  MODE_TYPE_GEO,
+  MODE_TYPE_PIVOT,
+} from "metabase-lib/lib/Mode/constants";
+
+export function getMode(question: Question): QueryMode | any | null {
+  const mode = getModeFromLib(question);
+  if (!mode) {
+    return null;
+  }
+
+  switch (mode) {
+    case MODE_TYPE_NATIVE:
+      return NativeMode;
+
+    case MODE_TYPE_SEGMENT:
+      return SegmentMode;
+
+    case MODE_TYPE_METRIC:
+      return MetricMode;
+
+    case MODE_TYPE_TIMESERIES:
+      return TimeseriesMode;
+
+    case MODE_TYPE_GEO:
+      return GeoMode;
+
+    case MODE_TYPE_PIVOT:
+      return PivotMode;
+
+    default:
+      return DefaultMode;
+  }
+}
diff --git a/frontend/src/metabase/static-viz/lib/dates.ts b/frontend/src/metabase/static-viz/lib/dates.ts
index 9f3bc2a55d9..451ad2e965a 100644
--- a/frontend/src/metabase/static-viz/lib/dates.ts
+++ b/frontend/src/metabase/static-viz/lib/dates.ts
@@ -14,7 +14,7 @@ const DEFAULT_OPTIONS = {
   time_enabled: false,
 };
 
-const DATE_FORMATS: { [key: string]: Intl.DateTimeFormat } = {
+const DATE_FORMATS: Record<string, Intl.DateTimeFormat> = {
   YY: new Intl.DateTimeFormat("en", { year: "2-digit" }),
   YYYY: new Intl.DateTimeFormat("en", { year: "numeric" }),
   M: new Intl.DateTimeFormat("en", { month: "numeric" }),
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
index 285664a9b01..a5a1f182a58 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
@@ -203,7 +203,7 @@ export function ObjectDetailFn({
   );
 
   const onKeyDown = (event: KeyboardEvent) => {
-    const capturedKeys: { [key: string]: () => void } = {
+    const capturedKeys: Record<string, () => void> = {
       ArrowUp: viewPreviousObjectDetail,
       ArrowDown: viewNextObjectDetail,
       Escape: closeObjectDetail,
-- 
GitLab