From dc8b72d6b84f1008f89e99b34438d798a8d2b94c Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Tue, 12 Apr 2022 15:20:28 +0300 Subject: [PATCH] Make event lines render for native queries (#21611) --- .../metabase/visualizations/lib/timelines.js | 69 +++++++++-------- .../collections/timelines.cy.spec.js | 75 ++++++++++++++++++- .../visualizations/line.cy.spec.js | 42 ----------- 3 files changed, 112 insertions(+), 74 deletions(-) diff --git a/frontend/src/metabase/visualizations/lib/timelines.js b/frontend/src/metabase/visualizations/lib/timelines.js index 2a6beade5f5..8f47faa26bc 100644 --- a/frontend/src/metabase/visualizations/lib/timelines.js +++ b/frontend/src/metabase/visualizations/lib/timelines.js @@ -14,10 +14,6 @@ function getAxis(chart) { return chart.svg().select(".axis.x"); } -function getBrush(chart) { - return chart.svg().select(".brush"); -} - function getScale(chart) { return chart.x(); } @@ -101,6 +97,21 @@ function hasEventText(events, eventIndex, eventPoints) { } } +function renderEventBrush({ chart }) { + const g = chart.g(); + const margins = chart.margins(); + const brush = g.selectAll(".event-brush").data([0]); + brush.exit().remove(); + + brush + .enter() + .insert("g", ":first-child") + .attr("class", "event-brush") + .attr("transform", `translate(${margins.left}, ${margins.top})`); + + return brush; +} + function renderEventLines({ chart, brush, @@ -134,7 +145,7 @@ function renderEventTicks({ onSelectTimelineEvents, onDeselectTimelineEvents, }) { - const eventAxis = axis.selectAll(".event-axis").data([eventGroups]); + const eventAxis = axis.selectAll(".event-axis").data([0]); const eventLines = brush.selectAll(".event-line").data(eventGroups); eventAxis.exit().remove(); @@ -216,40 +227,38 @@ export function renderEvents( onDeselectTimelineEvents, }, ) { - if (!isTimeseries) { + const axis = getAxis(chart); + + if (!axis || !isTimeseries) { return; } - const axis = getAxis(chart); - const brush = getBrush(chart); const scale = getScale(chart); const eventMapping = getEventMapping(events, scale); const eventPoints = getEventPoints(eventMapping); const eventGroups = getEventGroups(eventMapping); - if (brush) { - renderEventLines({ - chart, - brush, - eventPoints, - eventGroups, - selectedEventIds, - }); - } + const brush = renderEventBrush({ chart, eventGroups }); - if (axis) { - renderEventTicks({ - axis, - brush, - eventPoints, - eventGroups, - selectedEventIds, - onHoverChange, - onOpenTimelines, - onSelectTimelineEvents, - onDeselectTimelineEvents, - }); - } + renderEventLines({ + chart, + brush, + eventPoints, + eventGroups, + selectedEventIds, + }); + + renderEventTicks({ + axis, + brush, + eventPoints, + eventGroups, + selectedEventIds, + onHoverChange, + onOpenTimelines, + onSelectTimelineEvents, + onDeselectTimelineEvents, + }); } export function hasEventAxis({ timelineEvents = [], isTimeseries }) { diff --git a/frontend/test/metabase-visual/collections/timelines.cy.spec.js b/frontend/test/metabase-visual/collections/timelines.cy.spec.js index c29c59776f7..7d5a3a80c7a 100644 --- a/frontend/test/metabase-visual/collections/timelines.cy.spec.js +++ b/frontend/test/metabase-visual/collections/timelines.cy.spec.js @@ -1,4 +1,8 @@ -import { restore } from "__support__/e2e/cypress"; +import { restore, visitQuestionAdhoc } from "__support__/e2e/cypress"; +import { SAMPLE_DB_ID } from "__support__/e2e/cypress_data"; +import { SAMPLE_DATABASE } from "__support__/e2e/cypress_sample_database"; + +const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE; const EVENTS = [ { name: "Event 1", timestamp: "2020-01-01", icon: "star" }, @@ -19,7 +23,7 @@ describe("timelines", () => { cy.percySnapshot(); }); - it("should display timeline events", () => { + it("should display timeline events in collections", () => { cy.createTimelineWithEvents({ events: EVENTS }).then(({ timeline }) => { cy.visit(`/collection/root/timelines/${timeline.id}`); @@ -27,4 +31,71 @@ describe("timelines", () => { cy.percySnapshot(); }); }); + + it("should display timeline events with a structured question", () => { + cy.createTimelineWithEvents({ + timeline: { name: "Releases" }, + events: [ + { name: "v20", timestamp: "2017-10-30T00:00:00Z", icon: "cloud" }, + { name: "v21", timestamp: "2018-08-08T00:00:00Z", icon: "mail" }, + { name: "RC1", timestamp: "2019-05-10T00:00:00Z", icon: "bell" }, + { name: "RC2", timestamp: "2019-05-20T00:00:00Z", icon: "star" }, + ], + }); + + visitQuestionAdhoc({ + dataset_query: { + type: "query", + query: { + "source-table": ORDERS_ID, + aggregation: [["count"]], + breakout: [ + ["field", ORDERS.CREATED_AT, { "temporal-unit": "month" }], + ], + }, + database: SAMPLE_DB_ID, + }, + display: "line", + visualization_settings: { + "graph.dimensions": ["CREATED_AT"], + "graph.metrics": ["count"], + "graph.show_values": true, + }, + }); + + cy.findByLabelText("star icon").realHover(); + cy.findByText("RC1"); + cy.percySnapshot(); + }); + + it("should display timeline events with a native question", () => { + cy.createTimelineWithEvents({ + timeline: { name: "Releases" }, + events: [ + { name: "v20", timestamp: "2017-10-30T00:00:00Z", icon: "cloud" }, + { name: "v21", timestamp: "2018-08-08T00:00:00Z", icon: "mail" }, + { name: "RC1", timestamp: "2019-05-10T00:00:00Z", icon: "bell" }, + { name: "RC2", timestamp: "2019-05-20T00:00:00Z", icon: "star" }, + ], + }); + + visitQuestionAdhoc({ + dataset_query: { + type: "native", + native: { + query: "SELECT ID, CREATED_AT FROM ORDERS LIMIT 25", + }, + database: SAMPLE_DB_ID, + }, + display: "line", + visualization_settings: { + "graph.dimensions": ["CREATED_AT"], + "graph.metrics": ["ID"], + }, + }); + + cy.findByLabelText("star icon").realHover(); + cy.findByText("RC1"); + cy.percySnapshot(); + }); }); diff --git a/frontend/test/metabase-visual/visualizations/line.cy.spec.js b/frontend/test/metabase-visual/visualizations/line.cy.spec.js index 74039680b9e..708cf04d4a2 100644 --- a/frontend/test/metabase-visual/visualizations/line.cy.spec.js +++ b/frontend/test/metabase-visual/visualizations/line.cy.spec.js @@ -191,46 +191,4 @@ describe("visual tests > visualizations > line", () => { cy.percySnapshot(); }); - - it("with timeline events", () => { - cy.createTimelineWithEvents({ - timeline: { name: "Releases" }, - events: [ - { name: "v20", timestamp: "2017-10-30T00:00:00Z", icon: "cloud" }, - { name: "v21", timestamp: "2018-08-08T00:00:00Z", icon: "mail" }, - { name: "RC1", timestamp: "2019-05-10T00:00:00Z", icon: "bell" }, - { name: "RC2", timestamp: "2019-05-20T00:00:00Z", icon: "star" }, - ], - }); - - visitQuestionAdhoc({ - dataset_query: { - type: "query", - query: { - "source-table": ORDERS_ID, - aggregation: [["count"]], - breakout: [ - [ - "field", - ORDERS.CREATED_AT, - { - "temporal-unit": "month", - }, - ], - ], - }, - database: SAMPLE_DB_ID, - }, - display: "line", - visualization_settings: { - "graph.dimensions": ["CREATED_AT"], - "graph.metrics": ["count"], - "graph.show_values": true, - }, - }); - - cy.findByLabelText("star icon").realHover(); - cy.findByText("RC1"); - cy.percySnapshot(); - }); }); -- GitLab