From 7249b7df31dbe9651d36b066e83e6c0f8b7ce64e Mon Sep 17 00:00:00 2001
From: Romeo Van Snick <romeo@romeovansnick.be>
Date: Tue, 7 May 2024 18:00:32 +0200
Subject: [PATCH] Allow duplicate "combine column" columns in chill mode
 (#42314)

* Add suffix to name if column already exists

* Add test for duplicate combine columns

* Use direct question in test

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>

* Fix formatting

* Use createQuestion for other tests too

* Fix table references

---------

Co-authored-by: Kamil Mielnik <kamil@kamilmielnik.com>
---
 .../drillthroughs/combine-column.cy.spec.js   | 57 +++++++++++++++----
 .../CombineColumnsDrill.tsx                   |  1 +
 .../drills/combine-columns-drill/utils.ts     | 30 +++++++++-
 3 files changed, 76 insertions(+), 12 deletions(-)

diff --git a/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js
index 162c85b490f..fced9452592 100644
--- a/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js
+++ b/e2e/test/scenarios/visualizations-tabular/drillthroughs/combine-column.cy.spec.js
@@ -1,14 +1,17 @@
 import { SAMPLE_DB_ID } from "e2e/support/cypress_data";
+import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
 import {
+  createQuestion,
   describeWithSnowplow,
   expectGoodSnowplowEvent,
   expectNoBadSnowplowEvents,
-  openPeopleTable,
   popover,
   resetSnowplow,
   restore,
 } from "e2e/support/helpers";
 
+const { PEOPLE, PEOPLE_ID } = SAMPLE_DATABASE;
+
 describeWithSnowplow(
   "scenarios > visualizations > drillthroughs > table_drills > combine columns",
   () => {
@@ -23,16 +26,19 @@ describeWithSnowplow(
     });
 
     it("should be possible to combine columns from the a table header", () => {
-      openPeopleTable({ limit: 3, mode: "notebook" });
-
-      cy.findByLabelText("Pick columns").click();
-      popover().within(() => {
-        cy.findByText("Select none").click();
-        cy.findByLabelText("Email").click();
-      });
-
-      cy.findByLabelText("Pick columns").click();
-      cy.button("Visualize").click();
+      createQuestion(
+        {
+          query: {
+            "source-table": PEOPLE_ID,
+            fields: [
+              ["field", PEOPLE.ID, { "base-type": "type/Number" }],
+              ["field", PEOPLE.EMAIL, { "base-type": "type/Text" }],
+            ],
+            limit: 3,
+          },
+        },
+        { visitQuestion: true },
+      );
 
       cy.findAllByTestId("header-cell").contains("Email").click();
       popover().findByText("Combine columns").click();
@@ -86,5 +92,34 @@ describeWithSnowplow(
         database_id: SAMPLE_DB_ID,
       });
     });
+
+    it("should handle duplicate column names", () => {
+      createQuestion(
+        {
+          query: {
+            "source-table": PEOPLE_ID,
+            fields: [
+              ["field", PEOPLE.ID, { "base-type": "type/Number" }],
+              ["field", PEOPLE.EMAIL, { "base-type": "type/Text" }],
+            ],
+            limit: 3,
+          },
+        },
+        { visitQuestion: true },
+      );
+
+      // first combine (email + ID)
+      cy.findAllByTestId("header-cell").contains("Email").click();
+      popover().findByText("Combine columns").click();
+      popover().findByText("Done").click();
+
+      // second combine (email + ID)
+      cy.findAllByTestId("header-cell").contains("Email").click();
+      popover().findByText("Combine columns").click();
+      popover().findByText("Done").click();
+
+      cy.findAllByTestId("header-cell").contains("Email ID").should("exist");
+      cy.findAllByTestId("header-cell").contains("Email ID_2").should("exist");
+    });
   },
 );
diff --git a/frontend/src/metabase/querying/utils/drills/combine-columns-drill/components/CombineColumnsDrill/CombineColumnsDrill.tsx b/frontend/src/metabase/querying/utils/drills/combine-columns-drill/components/CombineColumnsDrill/CombineColumnsDrill.tsx
index 9e0bf517f9f..7778a405f49 100644
--- a/frontend/src/metabase/querying/utils/drills/combine-columns-drill/components/CombineColumnsDrill/CombineColumnsDrill.tsx
+++ b/frontend/src/metabase/querying/utils/drills/combine-columns-drill/components/CombineColumnsDrill/CombineColumnsDrill.tsx
@@ -105,6 +105,7 @@ export const CombineColumnsDrill = ({
       column,
       columnsAndSeparators,
     );
+
     const newQuery = Lib.expression(query, stageIndex, name, expressionClause);
 
     onSubmit(newQuery);
diff --git a/frontend/src/metabase/querying/utils/drills/combine-columns-drill/utils.ts b/frontend/src/metabase/querying/utils/drills/combine-columns-drill/utils.ts
index ca62695b49a..c5e41a9c9c4 100644
--- a/frontend/src/metabase/querying/utils/drills/combine-columns-drill/utils.ts
+++ b/frontend/src/metabase/querying/utils/drills/combine-columns-drill/utils.ts
@@ -139,9 +139,37 @@ export const getExpressionName = (
   column: Lib.ColumnMetadata,
   columnsAndSeparators: ColumnAndSeparator[],
 ): string => {
+  const columnNames = Lib.returnedColumns(query, stageIndex).map(
+    column => Lib.displayInfo(query, stageIndex, column).displayName,
+  );
+
+  const name = getCombinedColumnName(
+    query,
+    stageIndex,
+    column,
+    columnsAndSeparators,
+  );
+
+  return getNextName(columnNames, name, 1);
+};
+
+function getNextName(names: string[], name: string, index: number): string {
+  const suffixed = index === 1 ? name : `${name}_${index}`;
+  if (!names.includes(suffixed)) {
+    return suffixed;
+  }
+  return getNextName(names, name, index + 1);
+}
+
+function getCombinedColumnName(
+  query: Lib.Query,
+  stageIndex: number,
+  column: Lib.ColumnMetadata,
+  columnsAndSeparators: ColumnAndSeparator[],
+) {
   const columns = [column, ...columnsAndSeparators.map(({ column }) => column)];
   const names = columns.map(
     column => Lib.displayInfo(query, stageIndex, column).displayName,
   );
   return names.join(" ");
-};
+}
-- 
GitLab