From d1e8e441f645a25e59d50b5d0744cb38a2dbdc86 Mon Sep 17 00:00:00 2001 From: Oisin Coveney <oisin@metabase.com> Date: Wed, 17 Apr 2024 09:16:46 +0300 Subject: [PATCH] Convert `EmbedFrame.module.css` to CSS modules (#41367) --- .../embedding/embedding-dashboard.cy.spec.js | 13 +++++-- .../bar_chart.cy.spec.js | 4 +-- .../line_chart.cy.spec.js | 2 +- .../drillthroughs/chart_drill.cy.spec.js | 12 ++++--- .../pivot_tables.cy.spec.js | 4 +-- .../src/metabase/css/dashboard.module.css | 5 +-- .../ParameterValueWidget.module.css | 2 +- .../EmbedFrame/EmbedFrame.module.css | 33 ++++++++--------- .../components/EmbedFrame/EmbedFrame.tsx | 36 +++++++++++++++---- .../PublicApp/PublicApp.unit.spec.tsx | 15 ++++---- .../components/LineAreaBarChart.module.css | 8 ++--- .../lib/LineAreaBarPostRender.js | 26 ++++++++++++-- 12 files changed, 106 insertions(+), 54 deletions(-) diff --git a/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js b/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js index 1323acc5eb6..47d33221727 100644 --- a/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js +++ b/e2e/test/scenarios/embedding/embedding-dashboard.cy.spec.js @@ -614,12 +614,21 @@ describeEE("scenarios > embedding > dashboard appearance", () => { cy.log("Assert dashboard theme"); getIframeBody() .findByTestId("embed-frame") - .should("not.have.class", "Theme--transparent"); + .invoke("attr", "data-embed-theme") + .then(embedTheme => { + expect(embedTheme).to.eq("light"); + }); + // We're getting an input element which is 0x0 in size cy.findByLabelText("Transparent").click({ force: true }); + cy.wait(1000); getIframeBody() .findByTestId("embed-frame") - .should("have.class", "Theme--transparent"); + .invoke("attr", "data-embed-theme") + .then(embedTheme => { + expect(embedTheme).to.eq("transparent"); + }); + cy.get("@previewEmbedSpy").should("have.callCount", 1); cy.log("Assert dashboard title"); diff --git a/e2e/test/scenarios/visualizations-charts/bar_chart.cy.spec.js b/e2e/test/scenarios/visualizations-charts/bar_chart.cy.spec.js index de38e3bc59d..e29ccd34d5c 100644 --- a/e2e/test/scenarios/visualizations-charts/bar_chart.cy.spec.js +++ b/e2e/test/scenarios/visualizations-charts/bar_chart.cy.spec.js @@ -168,7 +168,7 @@ describe("scenarios > visualizations > bar chart", () => { .findByText(columnName) .should("not.exist"); cy.findAllByTestId("legend-item").should("have.length", 3); - cy.get(".enable-dots").should("have.length", 3); + cy.findAllByTestId("chart-series").should("have.length", 3); }); getDraggableElements() @@ -185,7 +185,7 @@ describe("scenarios > visualizations > bar chart", () => { .findByText(columnName) .should("exist"); cy.findAllByTestId("legend-item").should("have.length", 4); - cy.get(".enable-dots").should("have.length", 4); + cy.findAllByTestId("chart-series").should("have.length", 4); }); cy.findAllByTestId("legend-item").contains("Gadget").click(); diff --git a/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js b/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js index 765f1175d91..adf64eaec2a 100644 --- a/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js +++ b/e2e/test/scenarios/visualizations-charts/line_chart.cy.spec.js @@ -130,7 +130,7 @@ describe("scenarios > visualizations > line chart", () => { }); cy.findByTestId("query-visualization-root") - .get(".enable-dots") + .findAllByTestId("chart-series") .last() .find(".dot") .eq(3) diff --git a/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js index d0e437e1bad..d41757307da 100644 --- a/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js +++ b/e2e/test/scenarios/visualizations-tabular/drillthroughs/chart_drill.cy.spec.js @@ -163,7 +163,8 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => { visitDashboard(DASHBOARD_ID); cy.log("The first series line"); - cy.get(".sub.enable-dots._0") + cy.findAllByTestId("chart-series") + .eq(0) .find(".dot") .eq(0) .click({ force: true }); @@ -175,7 +176,8 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => { // Second line from the second question cy.log("The second series line"); - cy.get(".sub.enable-dots._1") + cy.findAllByTestId("chart-series") + .eq(1) .find(".dot") .eq(0) .click({ force: true }); @@ -232,7 +234,8 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => { visitDashboard(DASHBOARD_ID); cy.log("The first series line"); - cy.get(".sub.enable-dots._0") + cy.findAllByTestId("chart-series") + .eq(0) .find(".dot") .eq(0) .click({ force: true }); @@ -244,7 +247,8 @@ describe("scenarios > visualizations > drillthroughs > chart drill", () => { // Second line from the second question cy.log("The third series line"); - cy.get(".sub.enable-dots._2") + cy.findAllByTestId("chart-series") + .eq(2) .find(".dot") .eq(0) .click({ force: true }); diff --git a/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js b/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js index dfa378cc580..0caaa17dd9d 100644 --- a/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js +++ b/e2e/test/scenarios/visualizations-tabular/pivot_tables.cy.spec.js @@ -669,7 +669,7 @@ describe("scenarios > visualizations > pivot tables", { tags: "@slow" }, () => { .then($value => { cy.visit($value); }); - cy.get(".EmbedFrame-header").contains(test.subject); + cy.findByTestId("embed-frame-header").contains(test.subject); assertOnPivotFields(); }); @@ -699,7 +699,7 @@ describe("scenarios > visualizations > pivot tables", { tags: "@slow" }, () => { // visit the iframe src directly to ensure it's not sing preview endpoints visitIframe(); - cy.get(".EmbedFrame-header").contains(test.subject); + cy.findByTestId("embed-frame-header").contains(test.subject); assertOnPivotFields(); }); }); diff --git a/frontend/src/metabase/css/dashboard.module.css b/frontend/src/metabase/css/dashboard.module.css index 6aa8778bd6a..143f568fdc9 100644 --- a/frontend/src/metabase/css/dashboard.module.css +++ b/frontend/src/metabase/css/dashboard.module.css @@ -29,8 +29,9 @@ } .Dashboard.DashboardNight - :global(.enable-dots-onhover .dc-tooltip circle.dot:hover), -.Dashboard.DashboardNight :global(.enable-dots .dc-tooltip circle.dot) { + .enableDotsOnHover + :global(.dc-tooltip circle.dot:hover), +.Dashboard.DashboardNight .enableDots :global(.dc-tooltip circle.dot) { fill: currentColor; } diff --git a/frontend/src/metabase/parameters/components/ParameterValueWidget.module.css b/frontend/src/metabase/parameters/components/ParameterValueWidget.module.css index d2d1ae7b666..131a29d9eac 100644 --- a/frontend/src/metabase/parameters/components/ParameterValueWidget.module.css +++ b/frontend/src/metabase/parameters/components/ParameterValueWidget.module.css @@ -59,7 +59,7 @@ } .DashboardNight .parameter.noPopover input:focus, -:global(.Theme--night) .parameter.noPopover input:focus { +.ThemeNight .parameter.noPopover input:focus { color: var(--color-text-white); } diff --git a/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.module.css b/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.module.css index 8110f3130e3..b39ac88eddc 100644 --- a/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.module.css +++ b/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.module.css @@ -2,60 +2,57 @@ body { background-color: transparent; } -:global(.EmbedFrame) { +.EmbedFrame { background-color: white; } -:global(.EmbedFrame-header), -:global(.EmbedFrame-footer) { +.EmbedFrameHeader, +.EmbedFrameFooter { color: var(--color-text-dark); background-color: white; } -:global(.Theme--night.EmbedFrame) { +.ThemeNight.EmbedFrame { background-color: var(--color-bg-black); border: 1px solid var(--color-bg-dark); } -:global(.Theme--night .EmbedFrame-header), -:global(.Theme--night .EmbedFrame-footer) { +.ThemeNight .EmbedFrameHeader, +.ThemeNight .EmbedFrameFooter { color: color-mod(var(--color-text-white) alpha(-14%)); background-color: var(--color-bg-black); border-color: var(--color-bg-dark); } -:global(.Theme--night.EmbedFrame) .fullscreenNightText { +.ThemeNight.EmbedFrame .fullscreenNightText { color: color-mod(var(--color-text-white) alpha(-14%)); transition: color 1s linear; } -:global(.Theme--night.EmbedFrame svg text) { +.ThemeNight.EmbedFrame svg text { fill: color-mod(var(--color-text-white) alpha(-14%)) !important; stroke: none !important; } -:global(.Theme--night.EmbedFrame) .DashCard .Card { +.ThemeNight.EmbedFrame .DashCard .Card { background-color: var(--color-bg-black); border: 1px solid var(--color-bg-dark); } -:global(.Theme--night.EmbedFrame - .enable-dots-onhover - .dc-tooltip - circle.dot:hover), -:global(.Theme--night.EmbedFrame .enable-dots .dc-tooltip circle.dot) { +.ThemeNight.EmbedFrame .enableDotsOnHover :global(.dc-tooltip circle.dot:hover), +.ThemeNight.EmbedFrame .enableDots :global(.dc-tooltip circle.dot) { fill: currentColor; } -:global(.Theme--transparent.EmbedFrame) { +.ThemeTransparent.EmbedFrame { background-color: transparent; } -:global(.Theme--transparent .EmbedFrame-header), -:global(.Theme--transparent .EmbedFrame-footer) { +.ThemeTransparent .EmbedFrameHeader, +.ThemeTransparent .EmbedFrameFooter { background-color: transparent; } -:global(.Theme--transparent.EmbedFrame) .DashCard .Card { +.ThemeTransparent.EmbedFrame .DashCard .Card { background-color: transparent; } diff --git a/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.tsx b/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.tsx index 212d5e8d8c0..f2ad14666a1 100644 --- a/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.tsx +++ b/frontend/src/metabase/public/components/EmbedFrame/EmbedFrame.tsx @@ -35,7 +35,9 @@ import type { } from "metabase-types/api"; import type { State } from "metabase-types/store"; -import "./EmbedFrame.module.css"; +import ParameterValueWidgetS from "../../../parameters/components/ParameterValueWidget.module.css"; + +import EmbedFrameS from "./EmbedFrame.module.css"; import type { FooterVariant } from "./EmbedFrame.styled"; import { ActionButtonsContainer, @@ -84,7 +86,7 @@ type Props = OwnProps & interface HashOptions { bordered?: boolean; titled?: boolean; - theme?: string; + theme?: "night" | "transparent"; hide_parameters?: string; hide_download_button?: boolean; } @@ -95,6 +97,20 @@ function mapStateToProps(state: State) { }; } +const EMBED_THEME_CLASSES = (theme: HashOptions["theme"]) => { + if (!theme) { + return null; + } + + if (theme === "night") { + return cx(ParameterValueWidgetS.ThemeNight, EmbedFrameS.ThemeNight); + } + + if (theme === "transparent") { + return EmbedFrameS.ThemeTransparent; + } +}; + function EmbedFrame({ className, children, @@ -175,15 +191,18 @@ function EmbedFrame({ <Root hasScroll={hasFrameScroll} isBordered={bordered} - className={cx("EmbedFrame", className, { - [`Theme--${theme}`]: !!theme, - })} + className={cx( + EmbedFrameS.EmbedFrame, + className, + EMBED_THEME_CLASSES(theme), + )} data-testid="embed-frame" + data-embed-theme={theme} > <ContentContainer> {hasHeader && ( <Header - className="EmbedFrame-header" + className={EmbedFrameS.EmbedFrameHeader} data-testid="embed-frame-header" > {finalName && ( @@ -247,7 +266,10 @@ function EmbedFrame({ <Body>{children}</Body> </ContentContainer> {showFooter && ( - <Footer className="EmbedFrame-footer" variant={footerVariant}> + <Footer + className={EmbedFrameS.EmbedFrameFooter} + variant={footerVariant} + > {hasEmbedBranding && ( <LogoBadge variant={footerVariant} dark={theme === "night"} /> )} diff --git a/frontend/src/metabase/public/containers/PublicApp/PublicApp.unit.spec.tsx b/frontend/src/metabase/public/containers/PublicApp/PublicApp.unit.spec.tsx index 92e39a7e944..51c1cdb6db6 100644 --- a/frontend/src/metabase/public/containers/PublicApp/PublicApp.unit.spec.tsx +++ b/frontend/src/metabase/public/containers/PublicApp/PublicApp.unit.spec.tsx @@ -120,18 +120,15 @@ describe("PublicApp", () => { const embedFrame = screen.getByTestId("embed-frame"); - // eslint-disable-next-line jest-dom/prefer-to-have-class - expect(embedFrame.className).not.toEqual( - expect.stringContaining("Theme--"), - ); + expect(embedFrame).not.toHaveAttribute("data-embed-theme"); }); - test.each([ - ["night", "Theme--night"], - ["transparent", "Theme--transparent"], - ])("correctly handles %s theme", (theme, expectedClass) => { + test.each(["night", "transparent"])("correctly handles %s theme", theme => { setup({ hash: `#theme=${theme}` }); - expect(screen.getByTestId("embed-frame")).toHaveClass(expectedClass); + expect(screen.getByTestId("embed-frame")).toHaveAttribute( + "data-embed-theme", + theme, + ); }); }); }); diff --git a/frontend/src/metabase/visualizations/components/LineAreaBarChart.module.css b/frontend/src/metabase/visualizations/components/LineAreaBarChart.module.css index 286ca812d67..273557054a2 100644 --- a/frontend/src/metabase/visualizations/components/LineAreaBarChart.module.css +++ b/frontend/src/metabase/visualizations/components/LineAreaBarChart.module.css @@ -152,12 +152,12 @@ stroke-width: 3px; } -.LineAreaBarChart :global(.dc-chart .enable-dots .dc-tooltip .dot), +.LineAreaBarChart :global(.dc-chart) .enableDots :global(.dc-tooltip .dot), .LineAreaBarChart :global(.dc-chart .dc-tooltip .dot.selected), .LineAreaBarChart - :global(.dc-chart .enable-dots-onhover .dc-tooltip .dot:hover), -.LineAreaBarChart - :global(.dc-chart .enable-dots-onhover .dc-tooltip .dot.hover) { + :global(.dc-chart) + .enableDotsOnHover + :global(.dc-tooltip .dot:hover) { fill: white; stroke: currentColor; fill-opacity: 1 !important; diff --git a/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js b/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js index 384f8483b4e..08f123ff24d 100644 --- a/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js +++ b/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js @@ -1,9 +1,13 @@ +import cx from "classnames"; import d3 from "d3"; import _ from "underscore"; import CS from "metabase/css/core/index.css"; +import DashboardS from "metabase/css/dashboard.module.css"; import { color } from "metabase/lib/colors"; import { clipPathReference, moveToFront } from "metabase/lib/dom"; +import EmbedFrameS from "metabase/public/components/EmbedFrame/EmbedFrame.module.css"; +import LineAreaBarChartS from "metabase/visualizations/components/LineAreaBarChart.module.css"; import { adjustYAxisTicksIfNeeded } from "./apply_axis"; import { onRenderValueLabels } from "./chart_values"; @@ -154,8 +158,23 @@ function onRenderEnableDots(chart) { chart .svg() .select(`.sub._${index}`) - .classed("enable-dots", enableDots) - .classed("enable-dots-onhover", !enableDots); + .attr("data-enable-dots", enableDots) + .classed( + cx( + DashboardS.enableDots, + EmbedFrameS.enableDots, + LineAreaBarChartS.enableDots, + ), + enableDots, + ) + .classed( + cx( + DashboardS.enableDotsOnHover, + EmbedFrameS.enableDotsOnHover, + LineAreaBarChartS.enableDotsOnHover, + ), + !enableDots, + ); } } @@ -435,6 +454,9 @@ function onRender( onDeselectTimelineEvents, }, ) { + chart.svg().attr("data-testid", "chart"); + chart.selectAll(`.sub`).attr("data-testid", `chart-series`); + onRenderRemoveClipPath(chart); onRenderMoveContentToTop(chart); onRenderReorderCharts(chart); -- GitLab