diff --git a/frontend/src/metabase/modes/components/drill/AutomaticDashboardDrill.jsx b/frontend/src/metabase/modes/components/drill/AutomaticDashboardDrill.jsx index 803b348bd760baf1f86f31ca710172a0e5ffb36f..a53548196e1a58889bb6fc736a0380e4f4d51b2b 100644 --- a/frontend/src/metabase/modes/components/drill/AutomaticDashboardDrill.jsx +++ b/frontend/src/metabase/modes/components/drill/AutomaticDashboardDrill.jsx @@ -4,6 +4,7 @@ import type { ClickAction, ClickActionProps, } from "metabase-types/types/Visualization"; +import { isExpressionField } from "metabase/lib/query/field_ref"; import MetabaseSettings from "metabase/lib/settings"; @@ -15,11 +16,19 @@ export default ({ question, clicked }: ClickActionProps): ClickAction[] => { // questions with a breakout const dimensions = (clicked && clicked.dimensions) || []; - if ( + + // ExpressionDimensions don't work right now (see metabase#16680) + const includesExpressionDimensions = dimensions.some(dimension => { + return isExpressionField(dimension.column.field_ref); + }); + + const isUnsupportedDrill = !clicked || dimensions.length === 0 || - !MetabaseSettings.get("enable-xrays") - ) { + !MetabaseSettings.get("enable-xrays") || + includesExpressionDimensions; + + if (isUnsupportedDrill) { return []; } diff --git a/frontend/src/metabase/modes/components/drill/CompareToRestDrill.js b/frontend/src/metabase/modes/components/drill/CompareToRestDrill.js index 3129d4c0842569456258b304f4cb3b731c0940fd..f1d15bfdee8f2e5423a684ea0af4f5f207b21151 100644 --- a/frontend/src/metabase/modes/components/drill/CompareToRestDrill.js +++ b/frontend/src/metabase/modes/components/drill/CompareToRestDrill.js @@ -5,6 +5,7 @@ import type { ClickActionProps, } from "metabase-types/types/Visualization"; +import { isExpressionField } from "metabase/lib/query/field_ref"; import MetabaseSettings from "metabase/lib/settings"; export default ({ question, clicked }: ClickActionProps): ClickAction[] => { @@ -15,12 +16,20 @@ export default ({ question, clicked }: ClickActionProps): ClickAction[] => { // questions with a breakout const dimensions = (clicked && clicked.dimensions) || []; - if ( + + // ExpressionDimensions don't work right now (see metabase#16680) + const includesExpressionDimensions = dimensions.some(dimension => { + return isExpressionField(dimension.column.field_ref); + }); + + const isUnsupportedDrill = !clicked || dimensions.length === 0 || // xrays must be enabled for this to work - !MetabaseSettings.get("enable-xrays") - ) { + !MetabaseSettings.get("enable-xrays") || + includesExpressionDimensions; + + if (isUnsupportedDrill) { return []; } diff --git a/frontend/src/metabase/modes/lib/drilldown.js b/frontend/src/metabase/modes/lib/drilldown.js index 0c4e8dc1a1ce87771df93cbf6af9fc0feafe3e80..24a5f33c365beacce8ea8353d08939af07913516 100644 --- a/frontend/src/metabase/modes/lib/drilldown.js +++ b/frontend/src/metabase/modes/lib/drilldown.js @@ -4,7 +4,7 @@ import { isLatitude, isLongitude, isDate } from "metabase/lib/schema_metadata"; import _ from "underscore"; import { FieldDimension } from "metabase-lib/lib/Dimension"; - +import { isExpressionField } from "metabase/lib/query/field_ref"; // Drill-down progressions are defined as a series of steps, where each step has one or more dimension <-> breakout // transforms. // @@ -387,9 +387,13 @@ function matchingProgression(dimensions) { } function nextBreakouts(dimensionMaps, metadata) { - const dimensions = dimensionMaps.map(d => - columnToFieldDimension(d.column, metadata), - ); + const columns = dimensionMaps.map(d => d.column); + const dimensions = columnsToFieldDimensions(columns, metadata); + + const [firstDimension] = dimensions; + if (!firstDimension) { + return null; + } const [progression, currentStepNumber] = matchingProgression( dimensions, @@ -401,15 +405,9 @@ function nextBreakouts(dimensionMaps, metadata) { } const nextStepNumber = currentStepNumber + 1; - const [firstDimension] = dimensions; - if (!firstDimension) { - return null; - } - const table = metadata && firstDimension.field().table; - const tableDimensions = table.fields.map(field => - columnToFieldDimension(field, metadata), - ); + const tableDimensions = columnsToFieldDimensions(table.fields, metadata); + const allDimensions = [...dimensions, ...tableDimensions]; const nextStep = progression[nextStepNumber]; @@ -437,7 +435,17 @@ export function drillDownForDimensions(dimensions: any, metadata: any) { }; } +function columnsToFieldDimensions(columns, metadata) { + return columns + .map(column => columnToFieldDimension(column, metadata)) + .filter(Boolean); +} + function columnToFieldDimension(column, metadata) { + if (isExpressionField(column.field_ref)) { + return; + } + const dimension = new FieldDimension(column.id, null, metadata); if (column.unit) { diff --git a/frontend/src/metabase/visualizations/components/ChartClickActions.jsx b/frontend/src/metabase/visualizations/components/ChartClickActions.jsx index c42d2d7501006334bd4b52f23c22274e993b6a0c..779eb324c1cec74b12a8e8321e450a76dbad7e84 100644 --- a/frontend/src/metabase/visualizations/components/ChartClickActions.jsx +++ b/frontend/src/metabase/visualizations/components/ChartClickActions.jsx @@ -1,6 +1,7 @@ /* eslint-disable react/prop-types */ import React, { Component } from "react"; import { connect } from "react-redux"; +import { t } from "ttag"; import { Link } from "react-router"; import Icon from "metabase/components/Icon"; @@ -220,19 +221,19 @@ export default class ChartClickActions extends Component { )} > {SECTIONS[key].icon === "sum" && ( - <p className="mt0 text-medium text-small">Summarize</p> + <p className="mt0 text-medium text-small">{t`Summarize`}</p> )} {SECTIONS[key].icon === "breakout" && ( - <p className="my1 text-medium text-small">Break out by a…</p> + <p className="my1 text-medium text-small">{t`Break out by a…`}</p> )} {SECTIONS[key].icon === "bolt" && ( <p className="mt2 text-medium text-small"> - Automatic explorations + {t`Automatic explorations`} </p> )} {SECTIONS[key].icon === "funnel_outline" && ( <p className="mt0 text-dark text-small"> - Filter by this value + {t`Filter by this value`} </p> )} diff --git a/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js b/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js index 7609fca0341180e5e36edffcce68c9421119340f..8d0ce5af1b3607b7fe8dc2dda50655d3ecd1df8b 100644 --- a/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js +++ b/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js @@ -726,6 +726,57 @@ describe("scenarios > dashboard > dashboard drill", () => { }); }); + it("should drill-through on chart based on a custom column (metabase#13289)", () => { + cy.server(); + cy.route("POST", "/api/dataset").as("dataset"); + + cy.createQuestion({ + name: "13289Q", + display: "line", + query: { + "source-table": ORDERS_ID, + expressions: { + two: ["+", 1, 1], + }, + aggregation: [["count"]], + breakout: [ + ["expression", "two"], + [ + "field", + ORDERS.CREATED_AT, + { + "temporal-unit": "month", + }, + ], + ], + }, + }).then(({ body: { id: QUESTION_ID } }) => { + cy.createDashboard("13289D").then(({ body: { id: DASHBOARD_ID } }) => { + // Add question to the dashboard + cy.request("POST", `/api/dashboard/${DASHBOARD_ID}/cards`, { + cardId: QUESTION_ID, + row: 0, + col: 0, + sizeX: 12, + sizeY: 8, + }); + + cy.visit(`/dashboard/${DASHBOARD_ID}`); + }); + }); + + cy.get(".Card circle") + .eq(1) + .click({ force: true }); + + cy.findByText("Zoom in").click(); + + cy.wait("@dataset"); + + cy.findByText("two is equal to 2"); + cy.findByText("Created At is May, 2016"); + }); + describe("should preserve dashboard filter and apply it to the question on a drill-through (metabase#11503)", () => { const ordersIdFilter = { name: "Orders ID",