-
Anton Kulyk authored
* Move current click handler to legend title * Add `onToggleSeriesVisibility` prop to `Legend` * Update `LegendItemDot` component * Update `LegendItemTitle` * Toggle series visibility * Fix hover effect * Don't let to hide the last visible series * Add `visible` property to `BaseSeriesModel` * Fix dangling split axis label when hiding a series * Update `graph.series_order` viz setting widget * Fix can't toggle series visibility from legend popover * Fix legend dot clipped by `overflow: hidden` * Fix eslint error * Fix legend popover scroll * Add unit tests * Add e2e tests * Fix show/hide series for scatter charts * Default `LegendItemDot` to visible state * Only apply legend dot hover effect if there's a click handler * Don't allow to remove the last series in `ChartSettingSeriesOrder` * Fix tooltip performance drop * Fix e2e tests around `series_order` viz setting widget * Use `text-medium` for outer circle background * Update dot style * tweak inner circle color * Update copy * Use div instead of button if can't toggle visibility * Fix legend layout issue * Don't show trendlines for hidden series * Fix missing prop value * Disable visibility toggle outside of dashboard/qb view * Add public dashboard test * Add tooltip footer e2e helpers * Fix chart tooltip shows hidden series data --------- Co-authored-by:
Kyle Doherty <5248953+kdoh@users.noreply.github.com>
Anton Kulyk authored* Move current click handler to legend title * Add `onToggleSeriesVisibility` prop to `Legend` * Update `LegendItemDot` component * Update `LegendItemTitle` * Toggle series visibility * Fix hover effect * Don't let to hide the last visible series * Add `visible` property to `BaseSeriesModel` * Fix dangling split axis label when hiding a series * Update `graph.series_order` viz setting widget * Fix can't toggle series visibility from legend popover * Fix legend dot clipped by `overflow: hidden` * Fix eslint error * Fix legend popover scroll * Add unit tests * Add e2e tests * Fix show/hide series for scatter charts * Default `LegendItemDot` to visible state * Only apply legend dot hover effect if there's a click handler * Don't allow to remove the last series in `ChartSettingSeriesOrder` * Fix tooltip performance drop * Fix e2e tests around `series_order` viz setting widget * Use `text-medium` for outer circle background * Update dot style * tweak inner circle color * Update copy * Use div instead of button if can't toggle visibility * Fix legend layout issue * Don't show trendlines for hidden series * Fix missing prop value * Disable visibility toggle outside of dashboard/qb view * Add public dashboard test * Add tooltip footer e2e helpers * Fix chart tooltip shows hidden series data --------- Co-authored-by:
Kyle Doherty <5248953+kdoh@users.noreply.github.com>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
e2e-visual-tests-helpers.js 4.95 KiB
import { popover } from "e2e/support/helpers/e2e-ui-elements-helpers";
import { color as getColor } from "metabase/lib/colors";
import { Icons } from "metabase/ui";
import { GOAL_LINE_DASH } from "metabase/visualizations/echarts/cartesian/option/goal-line.ts";
import { TREND_LINE_DASH } from "metabase/visualizations/echarts/cartesian/option/trend-line.ts";
import {
setSvgColor,
svgToDataUri,
} from "metabase/visualizations/echarts/cartesian/timeline-events/option";
export function echartsContainer() {
return cy.findByTestId("chart-container");
}
export function echartsTriggerBlur() {
return echartsContainer().realHover({ position: "right" });
}
export function ensureEchartsContainerHasSvg() {
return echartsContainer().should(root => {
// Check if there's an SVG child within the element
expect(root.find("svg").length, "SVG exists").to.be.equal(1);
});
}
export function goalLine() {
return echartsContainer().find(
`path[stroke-dasharray='${GOAL_LINE_DASH.join(",")}']`,
);
}
export function trendLine() {
return echartsContainer().find(
`path[stroke-dasharray='${TREND_LINE_DASH.join(",")}']`,
);
}
export function getXYTransform(element) {
const transform = element.prop("transform");
const {
baseVal: [{ matrix }],
} = transform;
const { e: x, f: y } = matrix;
return { x, y };
}
export function echartsIcon(name, isSelected = false) {
const iconSvg = setSvgColor(
Icons[name].source,
getColor(isSelected ? "brand" : "text-light"),
);
const dataUri = svgToDataUri(iconSvg);
return echartsContainer().find(`image[href="${dataUri}"]`);
}
export function chartPathWithFillColor(color) {
return echartsContainer().find(`path[fill="${color}"]`);
}
export function chartPathsWithFillColors(colors) {
return colors.map(color => chartPathWithFillColor(color));
}
const CIRCLE_PATH = "M1 0A1 1 0 1 1 1 -0.0001";
export function cartesianChartCircle() {
return echartsContainer()
.find(`path[d="${CIRCLE_PATH}"]`)
.should("be.visible");
}
export function cartesianChartCircleWithColor(color) {
return echartsContainer()
.find(`path[d="${CIRCLE_PATH}"][stroke="${color}"]`)
.should("be.visible");
}
export function cartesianChartCircleWithColors(colors) {
return colors.map(color => cartesianChartCircleWithColor(color));
}
export function scatterBubbleWithColor(color) {
return echartsContainer().find(`path[d="${CIRCLE_PATH}"][fill="${color}"]`);
}
export function getValueLabels() {
return echartsContainer().find("text[stroke-width='3']");
}
export function testPairedTooltipValues(val1, val2) {
cy.contains(val1).closest("td").siblings("td").findByText(val2);
}
export function testTooltipPairs(rowPairs = []) {
popover().within(() => {
rowPairs.forEach(([label, value]) => {
testPairedTooltipValues(label, value);
});
});
}
export function testStackedTooltipRows(rows = []) {
popover().within(() => {
rows.forEach(([label, value, percent]) => {
cy.findByText(label)
.parent()
.within(() => {
cy.findByTestId("row-value").should("have.text", value);
cy.findByTestId("row-percent").should("have.text", percent);
});
});
});
}
export function pieSlices() {
return echartsContainer().find("path[stroke-linejoin='bevel']");
}
export function pieSliceWithColor(color) {
return echartsContainer().find(
`path[stroke-linejoin='bevel'][fill='${color}']`,
);
}
export function echartsTooltip() {
// ECharts may keep two dom instances of the tooltip
return cy
.findAllByTestId("echarts-tooltip")
.filter(":visible")
.should("have.length", 1)
.eq(0);
}
export function tooltipHeader() {
return cy.findByTestId("echarts-tooltip-header");
}
function tooltipFooter() {
return cy.findByTestId("echarts-tooltip-footer");
}
export function assertTooltipRow(
name,
{ color, value, secondaryValue, index } = {},
) {
cy.findAllByText(name)
.eq(index ?? 0)
.parent("tr")
.within(() => {
if (color) {
cy.get("td")
.eq(0)
.find("span")
.should("have.class", `marker-${color.replace("#", "")}`);
}
if (value) {
cy.findByText(value);
}
if (secondaryValue) {
cy.findByText(secondaryValue);
}
});
}
function assertTooltipFooter({ name, value, secondaryValue }) {
tooltipFooter().within(() => {
if (name) {
cy.findByText(name);
}
if (value) {
cy.findByText(value);
}
if (secondaryValue) {
cy.findByText(secondaryValue);
}
});
}
export function assertEChartsTooltip({ header, rows, footer, blurAfter }) {
echartsTooltip().within(() => {
if (header != null) {
tooltipHeader().should("have.text", header);
}
if (rows != null) {
rows.forEach(row => {
const { name, ...rest } = row;
assertTooltipRow(name, rest);
});
}
if (footer != null) {
assertTooltipFooter(footer);
}
});
if (blurAfter) {
echartsTriggerBlur();
}
}