diff --git a/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js b/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js index e1aab65ac25a9db003f606c36a059017447cdba6..af121f4f27214efbe5b7818ad4ccbe82746401e0 100644 --- a/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js +++ b/e2e/test/scenarios/dashboard-cards/click-behavior.cy.spec.js @@ -1,8 +1,12 @@ import { USER_GROUPS } from "e2e/support/cypress_data"; import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; -import { ORDERS_QUESTION_ID } from "e2e/support/cypress_sample_instance_data"; +import { + ORDERS_BY_YEAR_QUESTION_ID, + ORDERS_QUESTION_ID, +} from "e2e/support/cypress_sample_instance_data"; import { addOrUpdateDashboardCard, + createDashboardWithTabs, dashboardHeader, editDashboard, getActionCardDetails, @@ -390,7 +394,7 @@ describe("scenarios > dashboard > dashboard cards > click behavior", () => { idAlias: "targetDashboardId", }; - createDashboardWithTabs({ + createDashboardWithTabsLocal({ dashboard, tabs, dashcards: [ @@ -458,7 +462,11 @@ describe("scenarios > dashboard > dashboard cards > click behavior", () => { }; const tabs = [FIRST_TAB, SECOND_TAB, THIRD_TAB]; - createDashboardWithTabs({ dashboard: TARGET_DASHBOARD, tabs, options }); + createDashboardWithTabsLocal({ + dashboard: TARGET_DASHBOARD, + tabs, + options, + }); const TAB_SLUG_MAP = {}; tabs.forEach(tab => { @@ -569,7 +577,11 @@ describe("scenarios > dashboard > dashboard cards > click behavior", () => { idAlias: "targetDashboardId", }; - createDashboardWithTabs({ dashboard: TARGET_DASHBOARD, tabs, options }); + createDashboardWithTabsLocal({ + dashboard: TARGET_DASHBOARD, + tabs, + options, + }); const TAB_SLUG_MAP = {}; tabs.forEach(tab => { @@ -1238,7 +1250,7 @@ describe("scenarios > dashboard > dashboard cards > click behavior", () => { idAlias: "targetDashboardId", }; - createDashboardWithTabs({ + createDashboardWithTabsLocal({ dashboard, tabs, dashcards: [ @@ -1758,6 +1770,93 @@ describe("scenarios > dashboard > dashboard cards > click behavior", () => { ); }); }); + + it("should navigate to a different tab on the same dashboard when configured (metabase#39319)", () => { + const TAB_1 = { + id: 1, + name: "first-tab", + }; + const TAB_2 = { + id: 2, + name: "second-tab", + }; + const tabs = [TAB_1, TAB_2]; + const FILTER_MAPPING_COLUMN = "User ID"; + const DASHBOARD_TEXT_FILTER = { + id: "1", + name: "Text filter", + slug: "filter-text", + type: "string/contains", + }; + + createDashboardWithTabs({ + name: TARGET_DASHBOARD.name, + tabs, + parameters: [{ ...DASHBOARD_TEXT_FILTER }], + dashcards: [ + createMockDashboardCard({ + id: -1, + card_id: ORDERS_QUESTION_ID, + size_x: 12, + size_y: 6, + dashboard_tab_id: TAB_1.id, + parameter_mappings: [ + createTextFilterMapping({ card_id: ORDERS_QUESTION_ID }), + ], + }), + createMockDashboardCard({ + id: -2, + card_id: ORDERS_BY_YEAR_QUESTION_ID, + size_x: 12, + size_y: 6, + dashboard_tab_id: TAB_2.id, + parameter_mappings: [ + createTextFilterMapping({ card_id: ORDERS_BY_YEAR_QUESTION_ID }), + ], + }), + ], + }).then(dashboard => { + cy.wrap(dashboard.id).as("targetDashboardId"); + dashboard.tabs.forEach(tab => { + cy.wrap(tab.id).as(`${tab.name}-id`); + }); + visitDashboard(dashboard.id); + }); + + const TAB_SLUG_MAP = {}; + tabs.forEach(tab => { + cy.get(`@${tab.name}-id`).then(tabId => { + TAB_SLUG_MAP[tab.name] = `${tabId}-${tab.name}`; + }); + }); + + editDashboard(); + + getDashboardCard().realHover().icon("click").click(); + cy.get("aside").findByText(FILTER_MAPPING_COLUMN).click(); + addDashboardDestination(); + cy.get("aside") + .findByLabelText("Select a dashboard tab") + .should("have.value", TAB_1.name) + .click(); + cy.findByRole("listbox").findByText(TAB_2.name).click(); + cy.get("aside").findByText(DASHBOARD_TEXT_FILTER.name).click(); + popover().findByText(FILTER_MAPPING_COLUMN).click(); + + cy.get("aside").button("Done").click(); + saveDashboard({ waitMs: 250 }); + + // test click behavior routing to same dashboard, different tab + getTableCell(1).click(); + cy.get("@targetDashboardId").then(targetDashboardId => { + cy.location().should(({ pathname, search }) => { + expect(pathname).to.equal(`/dashboard/${targetDashboardId}`); + expect(search).to.equal( + `?tab=${TAB_SLUG_MAP[TAB_2.name]}&${DASHBOARD_FILTER_TEXT.slug}=${1}`, + ); + }); + }); + }); }); /** @@ -1963,14 +2062,14 @@ const getCountToDashboardFilterMapping = () => { return cy.get("aside").contains(`${COUNT_COLUMN_NAME} updates 1 filter`); }; -const createDashboardWithTabs = ({ +const createDashboardWithTabsLocal = ({ dashboard: dashboardDetails, tabs, dashcards = [], options, }) => { cy.createDashboard(dashboardDetails).then(({ body: dashboard }) => { - if (options.wrapId) { + if (options?.wrapId) { cy.wrap(dashboard.id).as(options.idAlias ?? "dashboardId"); } cy.request("PUT", `/api/dashboard/${dashboard.id}`, { diff --git a/frontend/src/metabase-lib/queries/drills/dashboard-click-drill.js b/frontend/src/metabase-lib/queries/drills/dashboard-click-drill.js index 3d0d036ea93f360323e6f38ca7b376663a6ab709..155c3e6579df368fbffbfd6408d097fe04de2179 100644 --- a/frontend/src/metabase-lib/queries/drills/dashboard-click-drill.js +++ b/frontend/src/metabase-lib/queries/drills/dashboard-click-drill.js @@ -47,6 +47,13 @@ export function getDashboardDrillType(clicked) { return null; } +export function getDashboardDrillTab(clicked) { + const clickBehavior = getClickBehavior(clicked); + const { tabId } = getClickBehaviorData(clicked, clickBehavior); + + return tabId; +} + export function getDashboardDrillParameters(clicked) { const clickBehavior = getClickBehavior(clicked); const { data, parameterMapping, extraData } = getClickBehaviorData( @@ -141,10 +148,10 @@ function getClickBehavior(clicked) { function getClickBehaviorData(clicked, clickBehavior) { const data = getDataFromClicked(clicked); - const { type, linkType, parameterMapping, targetId } = clickBehavior; + const { type, linkType, parameterMapping, tabId, targetId } = clickBehavior; const { extraData } = clicked || {}; - return { type, linkType, data, extraData, parameterMapping, targetId }; + return { type, linkType, data, extraData, parameterMapping, tabId, targetId }; } function getParameterIdValuePairs( diff --git a/frontend/src/metabase/visualizations/click-actions/actions/DashboardClickAction.tsx b/frontend/src/metabase/visualizations/click-actions/actions/DashboardClickAction.tsx index a7bd3135a0e66cb50c8f669b6c693b98d49c1254..fd0bf441ad6882ffd8e6c6b5da66da7dc47c4063 100644 --- a/frontend/src/metabase/visualizations/click-actions/actions/DashboardClickAction.tsx +++ b/frontend/src/metabase/visualizations/click-actions/actions/DashboardClickAction.tsx @@ -1,4 +1,5 @@ import { + selectTab, setOrUnsetParameterValues, setParameterValue, } from "metabase/dashboard/actions"; @@ -13,6 +14,7 @@ import { getDashboardDrillLinkUrl, getDashboardDrillParameters, getDashboardDrillQuestionUrl, + getDashboardDrillTab, getDashboardDrillType, getDashboardDrillUrl, } from "metabase-lib/queries/drills/dashboard-click-drill"; @@ -40,7 +42,9 @@ function getAction( url: () => getDashboardDrillQuestionUrl(question, clicked), }; case "dashboard-url": - return { url: () => getDashboardDrillUrl(clicked) }; + return { + url: () => getDashboardDrillUrl(clicked), + }; case "dashboard-filter": return { action: () => { @@ -51,6 +55,12 @@ function getAction( case "dashboard-reset": return { action: () => dispatch => { + const tabId = getDashboardDrillTab(clicked); + + if (tabId) { + dispatch(selectTab({ tabId })); + } + const parameterIdValuePairs = getDashboardDrillParameters(clicked); parameterIdValuePairs .map(([id, value]) => setParameterValue(id, value))