diff --git a/frontend/src/metabase/visualizations/components/FunnelNormal.jsx b/frontend/src/metabase/visualizations/components/FunnelNormal.jsx index 50ff711a696bd5517b11546ac4a0a81b1536d21d..033f01d4695ce2c1d7f9bcf76632c29dcd45d7d8 100644 --- a/frontend/src/metabase/visualizations/components/FunnelNormal.jsx +++ b/frontend/src/metabase/visualizations/components/FunnelNormal.jsx @@ -163,33 +163,38 @@ export default class FunnelNormal extends Component { <div className={styles.Subtitle}> </div> </div> </div> - {infos.slice(1).map((info, index) => ( - <div - key={index} - className={cx(styles.FunnelStep, "flex flex-column")} - > - <Ellipsified className={styles.Head}> - {formatDimension(rows[index + 1][dimensionIndex])} - </Ellipsified> - <GraphSection - className={cx({ "cursor-pointer": isClickable })} - index={index} - info={info} - infos={infos} - hovered={hovered} - onHoverChange={onHoverChange} - onVisualizationClick={isClickable ? onVisualizationClick : null} - /> - <div className={styles.Infos}> - <Ellipsified className={styles.Title}> - {formatPercent(info.value / initial.value)} - </Ellipsified> - <Ellipsified className={styles.Subtitle}> - {formatMetric(rows[index + 1][metricIndex])} + {infos.slice(1).map((info, index) => { + const stepPercentage = + initial.value > 0 ? info.value / initial.value : 0; + + return ( + <div + key={index} + className={cx(styles.FunnelStep, "flex flex-column")} + > + <Ellipsified className={styles.Head}> + {formatDimension(rows[index + 1][dimensionIndex])} </Ellipsified> + <GraphSection + className={cx({ "cursor-pointer": isClickable })} + index={index} + info={info} + infos={infos} + hovered={hovered} + onHoverChange={onHoverChange} + onVisualizationClick={isClickable ? onVisualizationClick : null} + /> + <div className={styles.Infos}> + <Ellipsified className={styles.Title}> + {formatPercent(stepPercentage)} + </Ellipsified> + <Ellipsified className={styles.Subtitle}> + {formatMetric(rows[index + 1][metricIndex])} + </Ellipsified> + </div> </div> - </div> - ))} + ); + })} </div> ); } diff --git a/frontend/test/metabase-visual/visualizations/funnel.cy.spec.js b/frontend/test/metabase-visual/visualizations/funnel.cy.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..63a891d00304d3a8a217032ec479531729c0429f --- /dev/null +++ b/frontend/test/metabase-visual/visualizations/funnel.cy.spec.js @@ -0,0 +1,62 @@ +import { restore, visitQuestionAdhoc } from "__support__/e2e/cypress"; + +describe("visual tests > visualizations > funnel", () => { + beforeEach(() => { + restore(); + cy.signInAsNormalUser(); + cy.server(); + cy.route("POST", "/api/dataset").as("dataset"); + }); + + it("empty", () => { + const testQuery = { + type: "native", + native: { + query: + "select 'a' col1, 0 col2 union all\n" + + "select 'b', 0 union all\n" + + "select 'c', 0", + }, + database: 1, + }; + + visitQuestionAdhoc({ + dataset_query: testQuery, + display: "funnel", + visualization_settings: { + "funnel.type": "funnel", + }, + }); + + cy.wait("@dataset"); + + cy.percySnapshot(); + }); + + it("normal", () => { + const testQuery = { + type: "native", + native: { + query: + "select 'a' step, 1000 users union all\n" + + "select 'b', 800 union all\n" + + "select 'c', 400 union all\n" + + "select 'd', 155 union all\n" + + "select 'e', 0", + }, + database: 1, + }; + + visitQuestionAdhoc({ + dataset_query: testQuery, + display: "funnel", + visualization_settings: { + "funnel.type": "funnel", + }, + }); + + cy.wait("@dataset"); + + cy.percySnapshot(); + }); +});