UNPKG

@neo4j-ndl/react-charts

Version:

React implementation of charts from Neo4j Design System

374 lines 17.3 kB
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()); }); }; import { jsx as _jsx } from "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/>. */ import { tokens } from '@neo4j-ndl/base'; import { Chart } from '@neo4j-ndl/react-charts'; import { expect } from '@playwright/experimental-ct-react'; import { ChartWrapperWithTooltipFormatter } from './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})`; }; export const 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)', }; export 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', }, ], }); export 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', }, }); export const 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', }, }; export const multiSeriesChart = ({ chartProps, width = '100%', height = '400px', }) => (_jsx("div", { style: { height, width }, className: "ndl-chart", children: _jsx(Chart, Object.assign({}, multiSeriesChartProps, chartProps)) })); export const pieChartWithTooltipFormatter = ({ seriesName, value, rgbSeriesColor, chartProps, width = '100%', height = '400px', }) => (_jsx("div", { style: { height, width }, children: _jsx(ChartWrapperWithTooltipFormatter, Object.assign({}, pieChartProps(seriesName, value, rgbSeriesColor), chartProps)) })); export const barChartWithTooltipFormatter = ({ seriesName, value, rgbSeriesColor, chartProps, width = '100%', height = '400px', }) => (_jsx("div", { style: { height, width }, children: _jsx(ChartWrapperWithTooltipFormatter, Object.assign({}, barChartProps(seriesName, value, rgbSeriesColor), chartProps)) })); export const barChart = ({ seriesName, value, rgbSeriesColor, chartProps, width = '100%', height = '400px', }) => (_jsx("div", { style: { height, width }, children: _jsx(Chart, Object.assign({}, barChartProps(seriesName, value, rgbSeriesColor), chartProps)) })); export const expectChartElementVisible = (page) => __awaiter(void 0, void 0, void 0, function* () { yield expect(page.locator('svg').first()).toBeVisible(); yield expect(page.locator('.ndl-chart-legend')).toBeVisible(); }); export 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); }); export 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, }; }); }); 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 = tokens.categorical[index]; if (!hexColor) { throw new Error(`Categorical palette index ${index} not found`); } return hexColor; }; /** * Checks so a single series is not blurred. */ export const expectSingleSeriesNotToBeBlurred = (page, seriesName) => __awaiter(void 0, void 0, void 0, function* () { const allChartSeries = multiSeriesChartProps.series; const seriesIndex = allChartSeries.findIndex((seriesItem) => seriesItem.name === seriesName) + 1; const rgbSeriesColor = 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 expect(pathLocator).toBeAttached({ timeout: 10000 }); yield expect(pathLocator).not.toHaveAttribute('stroke-opacity'); }); /** * Checks so that the seriesNamesToBeHighlighted are visible in the chart and that other series are blurred. */ export const expectLegendSeriesToBeHighlightedInChart = (page, seriesNamesToBeHighlighted) => __awaiter(void 0, void 0, void 0, function* () { const allChartSeries = multiSeriesChartProps.series; for (const name of seriesNamesToBeHighlighted) { const highlightedSeriesIndex = allChartSeries.findIndex((seriesItem) => seriesItem.name === name) + 1; const rgbSeriesColor = 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 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 = 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 expect(pathLocator).toHaveAttribute('stroke-opacity'); } }); /** * 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. */ export const expectLegendSeriesToBeVisibleInChart = (page, seriesNamesToBeVisible) => __awaiter(void 0, void 0, void 0, function* () { yield 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 expect(visibleLegendItem).not.toHaveClass(/ndl-chart-legend-item-deselected/); } } const hiddenSeries = 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 expect(hiddenLegendItem).toBeVisible(); } } }); // Check so legends are selected -> has checkmark and no strike-through classes export const expectLegendsSelected = (page, seriesNames) => __awaiter(void 0, void 0, void 0, function* () { for (const name of seriesNames) { yield expect(page.locator(SELECTORS.seriesCheckmark(name))).toBeVisible(); yield expect(page.locator(SELECTORS.seriesDeselected(name))).not.toBeVisible(); } }); // Check so legends are deselected -> has strike-through and no checkmark classes export const expectLegendsDeselected = (page, seriesNames) => __awaiter(void 0, void 0, void 0, function* () { for (const name of seriesNames) { yield expect(page.locator(SELECTORS.seriesDeselected(name))).toBeVisible(); yield expect(page.locator(SELECTORS.seriesCheckmark(name))).not.toBeVisible(); } }); // Check so all legends are neutral -> no checkmark and no strike-through classes export const expectAllLegendsNeutral = (page) => __awaiter(void 0, void 0, void 0, function* () { yield expect(page.locator(SELECTORS.CHECKMARK)).not.toBeVisible(); yield expect(page.locator(SELECTORS.DESELECTED)).not.toBeVisible(); }); /** * 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. */ export 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'); }); export const expectFormattedTooltipToBeVisible = (page, seriesName, value, rgbSeriesColor) => __awaiter(void 0, void 0, void 0, function* () { const rgbWithSpaces = rgbSeriesColor.replace(/,/g, ', '); yield 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 expect(tooltip).toBeVisible(); }); export 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 expect(chartElement).toBeVisible(); yield chartElement.hover(); const tooltip = page.locator('.ndl-charts-chart-tooltip'); yield expect(tooltip).toContainText(seriesName); yield expect(tooltip).toContainText(new Intl.NumberFormat('en-US').format(value)); }); export const waitForAnimationEnd = (page, selector) => { return page .locator(selector) .evaluate((element) => Promise.all(element.getAnimations().map((animation) => animation.finished))); }; //# sourceMappingURL=chart-test-utils.js.map