UNPKG

@neo4j-ndl/react-charts

Version:

React implementation of charts from Neo4j Design System

396 lines 20.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitForAnimationEnd = exports.expectDefaultFormattedTooltipToBeVisible = exports.expectFormattedTooltipToBeVisible = exports.clickSeries = exports.expectAllLegendsNeutral = exports.expectLegendsDeselected = exports.expectLegendsSelected = exports.expectLegendSeriesToBeVisibleInChart = exports.expectLegendSeriesToBeHighlightedInChart = exports.expectSingleSeriesNotToBeBlurred = exports.getLegendMeasuringObserverResult = exports.startLegendMeasuringObserver = exports.expectChartElementVisible = exports.barChart = exports.barChartWithTooltipFormatter = exports.pieChartWithTooltipFormatter = exports.multiSeriesChart = exports.multiSeriesChartProps = exports.barChartProps = exports.pieChartProps = exports.chartColorsAfterHighlighting = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); /** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ const base_1 = require("@neo4j-ndl/base"); const react_charts_1 = require("@neo4j-ndl/react-charts"); const experimental_ct_react_1 = require("@playwright/experimental-ct-react"); const ChartWrapperWithTooltipFormatter_1 = require("./ChartWrapperWithTooltipFormatter"); /** * Convert hex color to rgb format * * @param hex color in hex code format * @returns color in rgb format rgb(_, _, _) */ const convertHexToRGB = (hex) => { const normalizedHex = hex.replace(/^#/, ''); const red = parseInt(normalizedHex.substring(0, 2), 16); const green = parseInt(normalizedHex.substring(2, 4), 16); const blue = parseInt(normalizedHex.substring(4, 6), 16); return `rgb(${red}, ${green}, ${blue})`; }; exports.chartColorsAfterHighlighting = { 1: 'rgb(93,207,216)', 10: 'rgb(210,126,49)', 11: 'rgb(78,151,121)', 12: 'rgb(190,255,117)', 2: 'rgb(84,80,223)', 3: 'rgb(242,152,62)', 4: 'rgb(221,75,155)', 5: 'rgb(156,154,255)', 6: 'rgb(132,244,136)', 7: 'rgb(69,140,249)', 8: 'rgb(113,69,188)', 9: 'rgb(240,210,70)', }; const pieChartProps = (seriesName, value, rgbSeriesColor) => ({ dataset: [ { source: [ ['product', '2012'], [seriesName, value], ], }, ], legend: { show: true, }, series: [ { center: ['50%', '50%'], encode: { itemName: 'product', value: '2012', }, itemStyle: { color: rgbSeriesColor, }, radius: '90%', type: 'pie', }, ], }); exports.pieChartProps = pieChartProps; const barChartProps = (seriesName, value, rgbSeriesColor) => ({ dataset: { dimensions: ['Country', 'Sales'], source: [['Finland', value]], }, legend: { show: true, }, series: [ { encode: { x: 'Country', y: 'Sales', }, itemStyle: { color: rgbSeriesColor, }, name: seriesName, type: 'bar', }, ], xAxis: { type: 'category', }, yAxis: { type: 'value', }, }); exports.barChartProps = barChartProps; exports.multiSeriesChartProps = { dataset: { dimensions: ['day', 'first', 'second', 'third', 'fourth', 'fifth', 'sixth'], source: [ ['Monday', 20, 40, 60, 80, 100, 120], ['Tuesday', 30, 50, 70, 90, 110, 130], ['Wednesday', 25, 45, 65, 85, 105, 125], ['Thursday', 35, 55, 75, 95, 115, 135], ['Friday', 40, 60, 80, 100, 120, 140], ], }, legend: { show: true, }, option: {}, series: [ { encode: { x: 'day', y: 'first' }, name: 'First Series', type: 'line', }, { encode: { x: 'day', y: 'second' }, name: 'Second Series', type: 'line', }, { encode: { x: 'day', y: 'third' }, name: 'Third Series', type: 'line', }, { encode: { x: 'day', y: 'fourth' }, name: 'Fourth Series', type: 'line', }, { encode: { x: 'day', y: 'fifth' }, name: 'Fifth Series', type: 'line', }, { encode: { x: 'day', y: 'sixth' }, name: 'Sixth Series', type: 'line', }, ], xAxis: { type: 'category', }, yAxis: { type: 'value', }, }; const multiSeriesChart = ({ chartProps, width = '100%', height = '400px', }) => ((0, jsx_runtime_1.jsx)("div", { style: { height, width }, className: "ndl-chart", children: (0, jsx_runtime_1.jsx)(react_charts_1.Chart, Object.assign({}, exports.multiSeriesChartProps, chartProps)) })); exports.multiSeriesChart = multiSeriesChart; const pieChartWithTooltipFormatter = ({ seriesName, value, rgbSeriesColor, chartProps, width = '100%', height = '400px', }) => ((0, jsx_runtime_1.jsx)("div", { style: { height, width }, children: (0, jsx_runtime_1.jsx)(ChartWrapperWithTooltipFormatter_1.ChartWrapperWithTooltipFormatter, Object.assign({}, (0, exports.pieChartProps)(seriesName, value, rgbSeriesColor), chartProps)) })); exports.pieChartWithTooltipFormatter = pieChartWithTooltipFormatter; const barChartWithTooltipFormatter = ({ seriesName, value, rgbSeriesColor, chartProps, width = '100%', height = '400px', }) => ((0, jsx_runtime_1.jsx)("div", { style: { height, width }, children: (0, jsx_runtime_1.jsx)(ChartWrapperWithTooltipFormatter_1.ChartWrapperWithTooltipFormatter, Object.assign({}, (0, exports.barChartProps)(seriesName, value, rgbSeriesColor), chartProps)) })); exports.barChartWithTooltipFormatter = barChartWithTooltipFormatter; const barChart = ({ seriesName, value, rgbSeriesColor, chartProps, width = '100%', height = '400px', }) => ((0, jsx_runtime_1.jsx)("div", { style: { height, width }, children: (0, jsx_runtime_1.jsx)(react_charts_1.Chart, Object.assign({}, (0, exports.barChartProps)(seriesName, value, rgbSeriesColor), chartProps)) })); exports.barChart = barChart; const expectChartElementVisible = (page) => __awaiter(void 0, void 0, void 0, function* () { yield (0, experimental_ct_react_1.expect)(page.locator('svg').first()).toBeVisible(); yield (0, experimental_ct_react_1.expect)(page.locator('.ndl-chart-legend')).toBeVisible(); }); exports.expectChartElementVisible = expectChartElementVisible; const startLegendMeasuringObserver = (page_1, ...args_1) => __awaiter(void 0, [page_1, ...args_1], void 0, function* (page, options = {}) { yield page.evaluate(({ chartLayoutSelector }) => { var _a; const windowWithLegendObserver = window; const legendElement = document.querySelector('.ndl-chart-legend'); const chartLayout = chartLayoutSelector ? document.querySelector(chartLayoutSelector) : undefined; windowWithLegendObserver.__chartHeightDuringLegendMeasurement = undefined; windowWithLegendObserver.__legendMeasuringClassWasAdded = false; (_a = windowWithLegendObserver.__legendMeasuringObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); if (!legendElement) { return; } const observer = new MutationObserver(() => { if (!legendElement.classList.contains('ndl-chart-legend-calculating')) { return; } windowWithLegendObserver.__legendMeasuringClassWasAdded = true; if (chartLayout && windowWithLegendObserver.__chartHeightDuringLegendMeasurement === undefined) { windowWithLegendObserver.__chartHeightDuringLegendMeasurement = chartLayout.getBoundingClientRect().height; } }); observer.observe(legendElement, { attributeFilter: ['class'], attributes: true, }); windowWithLegendObserver.__legendMeasuringObserver = observer; }, options); }); exports.startLegendMeasuringObserver = startLegendMeasuringObserver; const getLegendMeasuringObserverResult = (page) => __awaiter(void 0, void 0, void 0, function* () { return page.evaluate(() => { var _a, _b; const windowWithLegendObserver = window; (_a = windowWithLegendObserver.__legendMeasuringObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); return { chartHeightDuringMeasurement: windowWithLegendObserver.__chartHeightDuringLegendMeasurement, didAddMeasuringClass: (_b = windowWithLegendObserver.__legendMeasuringClassWasAdded) !== null && _b !== void 0 ? _b : false, }; }); }); exports.getLegendMeasuringObserverResult = getLegendMeasuringObserverResult; const SELECTORS = { CHECKMARK: '.ndl-chart-legend > .ndl-chart-legend-item .ndl-chart-legend-item-square-checkmark', DESELECTED: '.ndl-chart-legend > .ndl-chart-legend-item-deselected', seriesCheckmark: (name) => `.ndl-chart-legend > [data-labelname="${name}"] .ndl-chart-legend-item-square-checkmark`, seriesDeselected: (name) => `.ndl-chart-legend > [data-labelname="${name}"].ndl-chart-legend-item-deselected`, }; const getGraphPaletteColorFromSeriesIndex = (index) => { const hexColor = base_1.tokens.categorical[index]; if (!hexColor) { throw new Error(`Categorical palette index ${index} not found`); } return hexColor; }; /** * Checks so a single series is not blurred. */ const expectSingleSeriesNotToBeBlurred = (page, seriesName) => __awaiter(void 0, void 0, void 0, function* () { const allChartSeries = exports.multiSeriesChartProps.series; const seriesIndex = allChartSeries.findIndex((seriesItem) => seriesItem.name === seriesName) + 1; const rgbSeriesColor = exports.chartColorsAfterHighlighting[seriesIndex]; const hexSeriesColor = getGraphPaletteColorFromSeriesIndex(seriesIndex.toString()); // More flexible color matching - try both with and without spaces const rgbWithSpaces = rgbSeriesColor.replace(/,/g, ', '); const hexRgb = convertHexToRGB(hexSeriesColor); const rgbWithoutSpaces = hexRgb.replace(/, /g, ','); const pathLocator = page .locator(`path[stroke="${rgbSeriesColor}"]`) .or(page.locator(`path[stroke="${rgbWithSpaces}"]`)) .or(page.locator(`path[stroke="${hexSeriesColor}"]`)) .or(page.locator(`path[stroke="${convertHexToRGB(hexSeriesColor)}"]`)) .or(page.locator(`path[stroke="${rgbWithoutSpaces}"]`)); // Wait for the element to be attached to the DOM before checking its attributes yield (0, experimental_ct_react_1.expect)(pathLocator).toBeAttached({ timeout: 10000 }); yield (0, experimental_ct_react_1.expect)(pathLocator).not.toHaveAttribute('stroke-opacity'); }); exports.expectSingleSeriesNotToBeBlurred = expectSingleSeriesNotToBeBlurred; /** * Checks so that the seriesNamesToBeHighlighted are visible in the chart and that other series are blurred. */ const expectLegendSeriesToBeHighlightedInChart = (page, seriesNamesToBeHighlighted) => __awaiter(void 0, void 0, void 0, function* () { const allChartSeries = exports.multiSeriesChartProps.series; for (const name of seriesNamesToBeHighlighted) { const highlightedSeriesIndex = allChartSeries.findIndex((seriesItem) => seriesItem.name === name) + 1; const rgbSeriesColor = exports.chartColorsAfterHighlighting[highlightedSeriesIndex]; const hexSeriesColor = getGraphPaletteColorFromSeriesIndex(highlightedSeriesIndex.toString()); const rgbWithSpaces = rgbSeriesColor.replace(/,/g, ', '); const hexRgb = convertHexToRGB(hexSeriesColor); const rgbWithoutSpaces = hexRgb.replace(/, /g, ','); const pathLocator = page .locator(`path[stroke="${rgbSeriesColor}"]`) .or(page.locator(`path[stroke="${rgbWithSpaces}"]`)) .or(page.locator(`path[stroke="${hexSeriesColor}"]`)) .or(page.locator(`path[stroke="${convertHexToRGB(hexSeriesColor)}"]`)) .or(page.locator(`path[stroke="${rgbWithoutSpaces}"]`)); yield (0, experimental_ct_react_1.expect)(pathLocator).not.toHaveAttribute('stroke-opacity'); } const seriesToBeBlurred = allChartSeries.filter((series) => !seriesNamesToBeHighlighted.includes(series.name)); for (const series of seriesToBeBlurred) { const blurredSeriesIndex = allChartSeries.findIndex((seriesItem) => seriesItem.name === series.name) + 1; const rgbSeriesColor = exports.chartColorsAfterHighlighting[blurredSeriesIndex]; const hexSeriesColor = getGraphPaletteColorFromSeriesIndex(blurredSeriesIndex.toString()); const rgbWithSpaces = rgbSeriesColor.replace(/,/g, ', '); const hexRgb = convertHexToRGB(hexSeriesColor); const rgbWithoutSpaces = hexRgb.replace(/, /g, ','); const pathLocator = page .locator(`path[stroke="${rgbSeriesColor}"]`) .or(page.locator(`path[stroke="${rgbWithSpaces}"]`)) .or(page.locator(`path[stroke="${hexSeriesColor}"]`)) .or(page.locator(`path[stroke="${convertHexToRGB(hexSeriesColor)}"]`)) .or(page.locator(`path[stroke="${rgbWithoutSpaces}"]`)); yield (0, experimental_ct_react_1.expect)(pathLocator).toHaveAttribute('stroke-opacity'); } }); exports.expectLegendSeriesToBeHighlightedInChart = expectLegendSeriesToBeHighlightedInChart; /** * Identifies if the series are visible / not visible in the chart by checking * component-owned legend state. ECharts rewrites generated SVG paths during * legend updates, which makes exact path color selectors flaky in Firefox. */ const expectLegendSeriesToBeVisibleInChart = (page, seriesNamesToBeVisible) => __awaiter(void 0, void 0, void 0, function* () { yield (0, experimental_ct_react_1.expect)(page.locator('svg path').first()).toBeAttached(); for (const name of seriesNamesToBeVisible) { const visibleLegendItem = page.locator(`.ndl-chart-legend > [data-labelname="${name}"]`); if ((yield visibleLegendItem.count()) > 0) { yield (0, experimental_ct_react_1.expect)(visibleLegendItem).not.toHaveClass(/ndl-chart-legend-item-deselected/); } } const hiddenSeries = exports.multiSeriesChartProps.series.filter((series) => !seriesNamesToBeVisible.includes(series.name)); for (const hiddenSeriesItem of hiddenSeries) { const hiddenLegendItem = page.locator(SELECTORS.seriesDeselected(hiddenSeriesItem.name)); if ((yield hiddenLegendItem.count()) > 0) { yield (0, experimental_ct_react_1.expect)(hiddenLegendItem).toBeVisible(); } } }); exports.expectLegendSeriesToBeVisibleInChart = expectLegendSeriesToBeVisibleInChart; // Check so legends are selected -> has checkmark and no strike-through classes const expectLegendsSelected = (page, seriesNames) => __awaiter(void 0, void 0, void 0, function* () { for (const name of seriesNames) { yield (0, experimental_ct_react_1.expect)(page.locator(SELECTORS.seriesCheckmark(name))).toBeVisible(); yield (0, experimental_ct_react_1.expect)(page.locator(SELECTORS.seriesDeselected(name))).not.toBeVisible(); } }); exports.expectLegendsSelected = expectLegendsSelected; // Check so legends are deselected -> has strike-through and no checkmark classes const expectLegendsDeselected = (page, seriesNames) => __awaiter(void 0, void 0, void 0, function* () { for (const name of seriesNames) { yield (0, experimental_ct_react_1.expect)(page.locator(SELECTORS.seriesDeselected(name))).toBeVisible(); yield (0, experimental_ct_react_1.expect)(page.locator(SELECTORS.seriesCheckmark(name))).not.toBeVisible(); } }); exports.expectLegendsDeselected = expectLegendsDeselected; // Check so all legends are neutral -> no checkmark and no strike-through classes const expectAllLegendsNeutral = (page) => __awaiter(void 0, void 0, void 0, function* () { yield (0, experimental_ct_react_1.expect)(page.locator(SELECTORS.CHECKMARK)).not.toBeVisible(); yield (0, experimental_ct_react_1.expect)(page.locator(SELECTORS.DESELECTED)).not.toBeVisible(); }); exports.expectAllLegendsNeutral = expectAllLegendsNeutral; /** * Clicks the series legend item and hover the chart to make sure no blur effect exists on other series in the chart. * This is needed to make sure the series has the correct color when looking for them in the chart in later steps. */ const clickSeries = (page, seriesName) => __awaiter(void 0, void 0, void 0, function* () { yield page .locator(`.ndl-chart-legend > [data-labelname="${seriesName}"]`) .click(); yield page.hover('.ndl-chart'); }); exports.clickSeries = clickSeries; const expectFormattedTooltipToBeVisible = (page, seriesName, value, rgbSeriesColor) => __awaiter(void 0, void 0, void 0, function* () { const rgbWithSpaces = rgbSeriesColor.replace(/,/g, ', '); yield (0, experimental_ct_react_1.expect)(page .locator(`path[fill="${rgbSeriesColor}"]`) .or(page.locator(`fill[stroke="${rgbWithSpaces}"]`))).toBeVisible(); yield page .locator(`path[fill="${rgbSeriesColor}"]`) .or(page.locator(`fill[stroke="${rgbWithSpaces}"]`)) .hover(); const tooltip = page.getByText(`${seriesName}formatted ${value}`); yield (0, experimental_ct_react_1.expect)(tooltip).toBeVisible(); }); exports.expectFormattedTooltipToBeVisible = expectFormattedTooltipToBeVisible; const expectDefaultFormattedTooltipToBeVisible = (page, seriesName, value, rgbSeriesColor) => __awaiter(void 0, void 0, void 0, function* () { const rgbWithSpaces = rgbSeriesColor.replace(/,/g, ', '); const chartElement = page .locator(`path[fill="${rgbSeriesColor}"]`) .or(page.locator(`path[fill="${rgbWithSpaces}"]`)); yield (0, experimental_ct_react_1.expect)(chartElement).toBeVisible(); yield chartElement.hover(); const tooltip = page.locator('.ndl-charts-chart-tooltip'); yield (0, experimental_ct_react_1.expect)(tooltip).toContainText(seriesName); yield (0, experimental_ct_react_1.expect)(tooltip).toContainText(new Intl.NumberFormat('en-US').format(value)); }); exports.expectDefaultFormattedTooltipToBeVisible = expectDefaultFormattedTooltipToBeVisible; const waitForAnimationEnd = (page, selector) => { return page .locator(selector) .evaluate((element) => Promise.all(element.getAnimations().map((animation) => animation.finished))); }; exports.waitForAnimationEnd = waitForAnimationEnd; //# sourceMappingURL=chart-test-utils.js.map