diff --git a/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx b/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx index 90b966c6c6cae..4dbbc461d9eec 100644 --- a/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx +++ b/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx @@ -1,6 +1,7 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; +import type {} from '../typeOverloads'; import { Watermark } from '@mui/x-license/Watermark'; import { ChartContainerProps } from '@mui/x-charts/ChartContainer'; import { ResizableContainer } from '@mui/x-charts/internals'; diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx index e81f631d0c30a..e7cbac28c1ece 100644 --- a/packages/x-charts/src/BarChart/BarPlot.tsx +++ b/packages/x-charts/src/BarChart/BarPlot.tsx @@ -7,7 +7,7 @@ import { BarElement, BarElementSlotProps, BarElementSlots } from './BarElement'; import { AxisDefaultized } from '../models/axis'; import { BarItemIdentifier } from '../models'; import getColor from './getColor'; -import { useChartId } from '../hooks'; +import { useChartId, useDrawingArea } from '../hooks'; import { AnimationData, CompletedBarData, MaskData } from './types'; import { BarClipPath } from './BarClipPath'; import { BarLabelItemProps, BarLabelSlotProps, BarLabelSlots } from './BarLabel/BarLabelItem'; @@ -92,6 +92,7 @@ const useAggregatedData = (): { useBarSeries() ?? ({ series: {}, stackingGroups: [], seriesOrder: [] } as SeriesFormatterResult<'bar'>); const axisData = useCartesianContext(); + const drawingArea = useDrawingArea(); const chartId = useChartId(); const { series, stackingGroups } = seriesData; @@ -102,6 +103,12 @@ const useAggregatedData = (): { const masks: Record = {}; const data = stackingGroups.flatMap(({ ids: groupIds }, groupIndex) => { + const xMin = drawingArea.left; + const xMax = drawingArea.left + drawingArea.width; + + const yMin = drawingArea.top; + const yMax = drawingArea.top + drawingArea.height; + return groupIds.flatMap((seriesId) => { const xAxisId = series[seriesId].xAxisId ?? defaultXAxisId; const yAxisId = series[seriesId].yAxisId ?? defaultYAxisId; @@ -132,54 +139,69 @@ const useAggregatedData = (): { const { stackedData } = series[seriesId]; - return stackedData.map((values, dataIndex: number) => { - const valueCoordinates = values.map((v) => (verticalLayout ? yScale(v)! : xScale(v)!)); - - const minValueCoord = Math.round(Math.min(...valueCoordinates)); - const maxValueCoord = Math.round(Math.max(...valueCoordinates)); - - const stackId = series[seriesId].stack; - - const result = { - seriesId, - dataIndex, - layout: series[seriesId].layout, - x: verticalLayout ? xScale(xAxis[xAxisId].data?.[dataIndex])! + barOffset : minValueCoord, - y: verticalLayout ? minValueCoord : yScale(yAxis[yAxisId].data?.[dataIndex])! + barOffset, - xOrigin: xScale(0)!, - yOrigin: yScale(0)!, - height: verticalLayout ? maxValueCoord - minValueCoord : barWidth, - width: verticalLayout ? barWidth : maxValueCoord - minValueCoord, - color: colorGetter(dataIndex), - value: series[seriesId].data[dataIndex], - maskId: `${chartId}_${stackId || seriesId}_${groupIndex}_${dataIndex}`, - }; - - if (!masks[result.maskId]) { - masks[result.maskId] = { - id: result.maskId, - width: 0, - height: 0, - hasNegative: false, - hasPositive: false, - layout: result.layout, + return stackedData + .map((values, dataIndex: number) => { + const valueCoordinates = values.map((v) => (verticalLayout ? yScale(v)! : xScale(v)!)); + + const minValueCoord = Math.round(Math.min(...valueCoordinates)); + const maxValueCoord = Math.round(Math.max(...valueCoordinates)); + + const stackId = series[seriesId].stack; + + const result = { + seriesId, + dataIndex, + layout: series[seriesId].layout, + x: verticalLayout + ? xScale(xAxis[xAxisId].data?.[dataIndex])! + barOffset + : minValueCoord, + y: verticalLayout + ? minValueCoord + : yScale(yAxis[yAxisId].data?.[dataIndex])! + barOffset, xOrigin: xScale(0)!, yOrigin: yScale(0)!, - x: 0, - y: 0, + height: verticalLayout ? maxValueCoord - minValueCoord : barWidth, + width: verticalLayout ? barWidth : maxValueCoord - minValueCoord, + color: colorGetter(dataIndex), + value: series[seriesId].data[dataIndex], + maskId: `${chartId}_${stackId || seriesId}_${groupIndex}_${dataIndex}`, }; - } - const mask = masks[result.maskId]; - mask.width = result.layout === 'vertical' ? result.width : mask.width + result.width; - mask.height = result.layout === 'vertical' ? mask.height + result.height : result.height; - mask.x = Math.min(mask.x === 0 ? Infinity : mask.x, result.x); - mask.y = Math.min(mask.y === 0 ? Infinity : mask.y, result.y); - mask.hasNegative = mask.hasNegative || (result.value ?? 0) < 0; - mask.hasPositive = mask.hasPositive || (result.value ?? 0) > 0; - - return result; - }); + if ( + result.x > xMax || + result.x + result.width < xMin || + result.y > yMax || + result.y + result.height < yMin + ) { + return null; + } + + if (!masks[result.maskId]) { + masks[result.maskId] = { + id: result.maskId, + width: 0, + height: 0, + hasNegative: false, + hasPositive: false, + layout: result.layout, + xOrigin: xScale(0)!, + yOrigin: yScale(0)!, + x: 0, + y: 0, + }; + } + + const mask = masks[result.maskId]; + mask.width = result.layout === 'vertical' ? result.width : mask.width + result.width; + mask.height = result.layout === 'vertical' ? mask.height + result.height : result.height; + mask.x = Math.min(mask.x === 0 ? Infinity : mask.x, result.x); + mask.y = Math.min(mask.y === 0 ? Infinity : mask.y, result.y); + mask.hasNegative = mask.hasNegative || (result.value ?? 0) < 0; + mask.hasPositive = mask.hasPositive || (result.value ?? 0) > 0; + + return result; + }) + .filter((rectangle) => rectangle !== null); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 663fe7543b21f..92248312e5dd4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1651,6 +1651,9 @@ importers: '@vitejs/plugin-react-swc': specifier: ^3.7.1 version: 3.7.1(@swc/helpers@0.5.5)(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0)) + '@vitest/browser': + specifier: 2.1.4 + version: 2.1.4(@types/node@20.17.6)(playwright@1.48.2)(typescript@5.6.3)(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0))(vitest@2.1.4) '@vitest/ui': specifier: 2.1.4 version: 2.1.4(vitest@2.1.4) @@ -1665,7 +1668,7 @@ importers: version: 18.3.1(react@18.3.1) vitest: specifier: 2.1.4 - version: 2.1.4(@types/node@20.17.6)(@vitest/ui@2.1.4)(jsdom@24.1.3)(terser@5.27.0) + version: 2.1.4(@types/node@20.17.6)(@vitest/browser@2.1.4)(@vitest/ui@2.1.4)(jsdom@24.1.3)(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(terser@5.27.0) packages: @@ -2407,6 +2410,15 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bundled-es-modules/cookie@2.0.1': + resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + + '@bundled-es-modules/tough-cookie@0.1.6': + resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} + '@codspeed/core@3.1.1': resolution: {integrity: sha512-ONhERVDAtkm0nc+FYPivDozoMOlNUP2BWRBFDJYATGA18Iap5Kd2mZ1/Lwz54RB5+g+3YDOpsvotHa4hd3Q+7Q==} @@ -2985,6 +2997,26 @@ packages: cpu: [x64] os: [win32] + '@inquirer/confirm@5.0.1': + resolution: {integrity: sha512-6ycMm7k7NUApiMGfVc32yIPp28iPKxhGRMqoNDiUjq2RyTAkbs5Fx0TdzBqhabcKvniDdAAvHCmsRjnNfTsogw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': ^20.17.6 + + '@inquirer/core@10.0.1': + resolution: {integrity: sha512-KKTgjViBQUi3AAssqjUFMnMO3CM3qwCHvePV9EW+zTKGKafFGFF01sc1yOIYjLJ7QU52G/FbzKc+c01WLzXmVQ==} + engines: {node: '>=18'} + + '@inquirer/figures@1.0.7': + resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} + engines: {node: '>=18'} + + '@inquirer/type@3.0.0': + resolution: {integrity: sha512-YYykfbw/lefC7yKj7nanzQXILM7r3suIvyFlCcMskc99axmsSewXWkAfXKwMbgxL76iAFVmRwmYdwNZNc8gjog==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': ^20.17.6 + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -3029,6 +3061,10 @@ packages: resolution: {integrity: sha512-DPnl5lPX4v49eVxEbJnAizrpMdMTBz1qykZrAbBul9rfgk531v8oAt+Pm6O/rpAleRombNM7FJb5rYGzBJatOQ==} engines: {node: '>=18.0.0'} + '@mswjs/interceptors@0.36.10': + resolution: {integrity: sha512-GXrJgakgJW3DWKueebkvtYgGKkxA7s0u5B0P5syJM5rvQUnrpLPigvci8Hukl7yEM+sU06l+er2Fgvx/gmiRgg==} + engines: {node: '>=18'} + '@mui/base@5.0.0-beta.40': resolution: {integrity: sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==} engines: {node: '>=12.0.0'} @@ -3647,6 +3683,15 @@ packages: '@octokit/types@9.3.2': resolution: {integrity: sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@opentelemetry/api@1.8.0': resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} engines: {node: '>=8.0.0'} @@ -4019,6 +4064,9 @@ packages: '@types/cookie@0.4.1': resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} @@ -4208,12 +4256,18 @@ packages: '@types/sinonjs__fake-timers@8.1.5': resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + '@types/stylis@4.2.5': resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} '@types/stylis@4.2.6': resolution: {integrity: sha512-4nebF2ZJGzQk0ka0O6+FZUWceyFv4vWq/0dXBMmrSeAwzOuOd/GxE5Pa64d/ndeNLG73dXoBsRzvtsVsYUv6Uw==} + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -4328,6 +4382,21 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 + '@vitest/browser@2.1.4': + resolution: {integrity: sha512-89SrvShW6kWzmEYtBj5k1gBq88emoC2qrngw5hE1vNpRFteQ5/1URbKIVww391rIALTpzhhCt5yJt5tjLPZxYw==} + peerDependencies: + playwright: '*' + safaridriver: '*' + vitest: 2.1.4 + webdriverio: '*' + peerDependenciesMeta: + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + '@vitest/expect@2.1.4': resolution: {integrity: sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==} @@ -5082,6 +5151,10 @@ packages: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -5301,6 +5374,10 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + core-js-compat@3.38.1: resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} @@ -6547,6 +6624,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@16.9.0: + resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gtoken@7.0.1: resolution: {integrity: sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==} engines: {node: '>=14.0.0'} @@ -6613,6 +6694,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hermes-estree@0.20.1: resolution: {integrity: sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg==} @@ -6895,6 +6979,9 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} @@ -7884,6 +7971,16 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + msw@2.6.2: + resolution: {integrity: sha512-RdRgPvjfuzMIACkWv7VOVAeSRYMU3ofokLv1w0RsbFX960qnj/tFEyOFXY0G2GTUd9trA6rHuHciM/FKpBp6/A==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + multimatch@5.0.0: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} @@ -7898,6 +7995,10 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -8179,6 +8280,9 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + override-require@1.1.1: resolution: {integrity: sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg==} @@ -8404,6 +8508,9 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-to-regexp@8.1.0: resolution: {integrity: sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==} engines: {node: '>=16'} @@ -9272,6 +9379,9 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-replace-loader@3.1.0: resolution: {integrity: sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ==} peerDependencies: @@ -9661,6 +9771,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type-fest@4.26.1: + resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} + engines: {node: '>=16'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -10208,6 +10322,10 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + yoctocolors@2.0.2: resolution: {integrity: sha512-Ct97huExsu7cWeEjmrXlofevF8CvzUglJ4iGUet5B8xn1oumtAZBpHU4GzYuoE6PVqcZ5hghtBrSlhwHuR1Jmw==} engines: {node: '>=18'} @@ -11217,6 +11335,19 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@bundled-es-modules/cookie@2.0.1': + dependencies: + cookie: 0.7.2 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + + '@bundled-es-modules/tough-cookie@0.1.6': + dependencies: + '@types/tough-cookie': 4.0.5 + tough-cookie: 4.1.4 + '@codspeed/core@3.1.1': dependencies: axios: 1.7.7(debug@4.3.7) @@ -11230,7 +11361,7 @@ snapshots: dependencies: '@codspeed/core': 3.1.1 vite: 5.3.4(@types/node@20.17.6)(terser@5.27.0) - vitest: 2.1.4(@types/node@20.17.6)(@vitest/ui@2.1.4)(jsdom@24.1.3)(terser@5.27.0) + vitest: 2.1.4(@types/node@20.17.6)(@vitest/browser@2.1.4)(@vitest/ui@2.1.4)(jsdom@24.1.3)(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(terser@5.27.0) transitivePeerDependencies: - debug @@ -11677,6 +11808,32 @@ snapshots: '@img/sharp-win32-x64@0.33.5': optional: true + '@inquirer/confirm@5.0.1(@types/node@20.17.6)': + dependencies: + '@inquirer/core': 10.0.1(@types/node@20.17.6) + '@inquirer/type': 3.0.0(@types/node@20.17.6) + '@types/node': 20.17.6 + + '@inquirer/core@10.0.1(@types/node@20.17.6)': + dependencies: + '@inquirer/figures': 1.0.7 + '@inquirer/type': 3.0.0(@types/node@20.17.6) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + transitivePeerDependencies: + - '@types/node' + + '@inquirer/figures@1.0.7': {} + + '@inquirer/type@3.0.0(@types/node@20.17.6)': + dependencies: + '@types/node': 20.17.6 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -11807,6 +11964,15 @@ snapshots: - supports-color - typescript + '@mswjs/interceptors@0.36.10': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + '@mui/base@5.0.0-beta.40(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.26.0 @@ -12595,6 +12761,15 @@ snapshots: dependencies: '@octokit/openapi-types': 18.1.1 + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + '@opentelemetry/api@1.8.0': optional: true @@ -12967,6 +13142,8 @@ snapshots: '@types/cookie@0.4.1': {} + '@types/cookie@0.6.0': {} + '@types/cors@2.8.17': dependencies: '@types/node': 20.17.6 @@ -13167,10 +13344,14 @@ snapshots: '@types/sinonjs__fake-timers@8.1.5': {} + '@types/statuses@2.0.5': {} + '@types/stylis@4.2.5': {} '@types/stylis@4.2.6': {} + '@types/tough-cookie@4.0.5': {} + '@types/unist@3.0.3': {} '@types/webpack-bundle-analyzer@4.7.0(@swc/core@1.7.35(@swc/helpers@0.5.5))(webpack-cli@5.1.4(webpack-bundle-analyzer@4.10.2)(webpack@5.96.1))': @@ -13336,6 +13517,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/browser@2.1.4(@types/node@20.17.6)(playwright@1.48.2)(typescript@5.6.3)(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0))(vitest@2.1.4)': + dependencies: + '@testing-library/dom': 10.4.0 + '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) + '@vitest/mocker': 2.1.4(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0)) + '@vitest/utils': 2.1.4 + magic-string: 0.30.12 + msw: 2.6.2(@types/node@20.17.6)(typescript@5.6.3) + sirv: 3.0.0 + tinyrainbow: 1.2.0 + vitest: 2.1.4(@types/node@20.17.6)(@vitest/browser@2.1.4)(@vitest/ui@2.1.4)(jsdom@24.1.3)(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(terser@5.27.0) + ws: 8.18.0 + optionalDependencies: + playwright: 1.48.2 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - typescript + - utf-8-validate + - vite + '@vitest/expect@2.1.4': dependencies: '@vitest/spy': 2.1.4 @@ -13343,12 +13545,13 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.4(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0))': + '@vitest/mocker@2.1.4(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0))': dependencies: '@vitest/spy': 2.1.4 estree-walker: 3.0.3 magic-string: 0.30.12 optionalDependencies: + msw: 2.6.2(@types/node@20.17.6)(typescript@5.6.3) vite: 5.3.4(@types/node@20.17.6)(terser@5.27.0) '@vitest/pretty-format@2.1.4': @@ -13379,7 +13582,7 @@ snapshots: sirv: 3.0.0 tinyglobby: 0.2.10 tinyrainbow: 1.2.0 - vitest: 2.1.4(@types/node@20.17.6)(@vitest/ui@2.1.4)(jsdom@24.1.3)(terser@5.27.0) + vitest: 2.1.4(@types/node@20.17.6)(@vitest/browser@2.1.4)(@vitest/ui@2.1.4)(jsdom@24.1.3)(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(terser@5.27.0) '@vitest/utils@2.1.4': dependencies: @@ -14230,6 +14433,8 @@ snapshots: cli-width@3.0.0: {} + cli-width@4.1.0: {} + client-only@0.0.1: {} clipboard-copy@4.0.1: {} @@ -14468,6 +14673,8 @@ snapshots: cookie@0.7.1: {} + cookie@0.7.2: {} + core-js-compat@3.38.1: dependencies: browserslist: 4.24.0 @@ -16050,6 +16257,8 @@ snapshots: graphemer@1.4.0: {} + graphql@16.9.0: {} + gtoken@7.0.1(encoding@0.1.13): dependencies: gaxios: 6.1.1(encoding@0.1.13) @@ -16108,6 +16317,8 @@ snapshots: he@1.2.0: {} + headers-polyfill@4.0.3: {} + hermes-estree@0.20.1: {} hermes-parser@0.20.1: @@ -16402,6 +16613,8 @@ snapshots: is-negative-zero@2.0.3: {} + is-node-process@1.2.0: {} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -17657,6 +17870,31 @@ snapshots: ms@2.1.3: {} + msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.0.1(@types/node@20.17.6) + '@mswjs/interceptors': 0.36.10 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + chalk: 4.1.2 + graphql: 16.9.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + strict-event-emitter: 0.5.1 + type-fest: 4.26.1 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - '@types/node' + multimatch@5.0.0: dependencies: '@types/minimatch': 3.0.5 @@ -17674,6 +17912,8 @@ snapshots: mute-stream@1.0.0: {} + mute-stream@2.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -18078,6 +18318,8 @@ snapshots: os-tmpdir@1.0.2: {} + outvariant@1.4.3: {} + override-require@1.1.1: {} p-event@5.0.1: @@ -18301,6 +18543,8 @@ snapshots: path-to-regexp@3.3.0: {} + path-to-regexp@6.3.0: {} + path-to-regexp@8.1.0: {} path-type@3.0.0: @@ -19285,6 +19529,8 @@ snapshots: streamsearch@1.1.0: {} + strict-event-emitter@0.5.1: {} + string-replace-loader@3.1.0(webpack@5.96.1): dependencies: loader-utils: 2.0.4 @@ -19661,6 +19907,8 @@ snapshots: type-fest@2.19.0: {} + type-fest@4.26.1: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -19912,10 +20160,10 @@ snapshots: fsevents: 2.3.3 terser: 5.27.0 - vitest@2.1.4(@types/node@20.17.6)(@vitest/ui@2.1.4)(jsdom@24.1.3)(terser@5.27.0): + vitest@2.1.4(@types/node@20.17.6)(@vitest/browser@2.1.4)(@vitest/ui@2.1.4)(jsdom@24.1.3)(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(terser@5.27.0): dependencies: '@vitest/expect': 2.1.4 - '@vitest/mocker': 2.1.4(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0)) + '@vitest/mocker': 2.1.4(msw@2.6.2(@types/node@20.17.6)(typescript@5.6.3))(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0)) '@vitest/pretty-format': 2.1.4 '@vitest/runner': 2.1.4 '@vitest/snapshot': 2.1.4 @@ -19936,6 +20184,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.17.6 + '@vitest/browser': 2.1.4(@types/node@20.17.6)(playwright@1.48.2)(typescript@5.6.3)(vite@5.3.4(@types/node@20.17.6)(terser@5.27.0))(vitest@2.1.4) '@vitest/ui': 2.1.4(vitest@2.1.4) jsdom: 24.1.3 transitivePeerDependencies: @@ -20276,6 +20525,8 @@ snapshots: yocto-queue@1.0.0: {} + yoctocolors-cjs@2.1.2: {} + yoctocolors@2.0.2: {} zip-stream@4.1.1: diff --git a/test/performance-charts/README.md b/test/performance-charts/README.md new file mode 100644 index 0000000000000..997285c417f0a --- /dev/null +++ b/test/performance-charts/README.md @@ -0,0 +1,39 @@ +# Performance Charts + +## How to run + +First build the project. + +```bash +pnpm release:build +``` + +To run the tests locally, you have two options + +1. Initialize a git repo inside the `test/performance-charts` folder. + +2. Or uncomment all the `browser` options from [vitest.config.ts](./vitest.config.ts) file. + +```ts + browser: { + enabled: true, + headless: true, + name: 'chromium', + provider: 'playwright', + providerOptions: { + timeout: 60000, + }, + }, +``` + +Then `cd` into the project folder and run the tests. + +```bash +pnpm test:performance +``` + +or from the root of the project + +```bash +pnpm --filter "@mui-x-internal/performance-charts" test:performance +``` diff --git a/test/performance-charts/package.json b/test/performance-charts/package.json index 090dc439678a2..bb79214b2c07f 100644 --- a/test/performance-charts/package.json +++ b/test/performance-charts/package.json @@ -16,6 +16,7 @@ "@testing-library/user-event": "^14.5.2", "@vitejs/plugin-react": "^4.3.3", "@vitejs/plugin-react-swc": "^3.7.1", + "@vitest/browser": "2.1.4", "@vitest/ui": "2.1.4", "jsdom": "^24.1.3", "react": "^18.3.1", diff --git a/test/performance-charts/tests/BarChartPro.bench.tsx b/test/performance-charts/tests/BarChartPro.bench.tsx new file mode 100644 index 0000000000000..cc9ba6efb76fe --- /dev/null +++ b/test/performance-charts/tests/BarChartPro.bench.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports +import { render, cleanup } from '@testing-library/react'; +import { afterEach, bench, describe } from 'vitest'; +import { BarChartPro } from '@mui/x-charts-pro/BarChartPro'; +import { LicenseInfo, generateLicense } from '@mui/x-license'; +import { options } from '../utils/options'; + +describe('BarChartPro', () => { + afterEach(() => { + cleanup(); + }); + + const dataLength = 500; + const data = Array.from({ length: dataLength + 1 }).map((_, i) => ({ + x: i, + y: 50 + Math.sin(i / 5) * 25, + })); + + const xData = data.map((d) => d.x); + const yData = data.map((d) => d.y); + + bench( + 'BarChartPro with big data amount', + async () => { + const licenseKey = generateLicense({ + expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), + orderNumber: 'MUI-123', + planScope: 'pro', + licenseModel: 'subscription', + planVersion: 'Q3-2024', + }); + + LicenseInfo.setLicenseKey(licenseKey); + + const { findByText } = render( + , + ); + + await findByText('60', { ignore: 'span' }); + }, + options, + ); +}); diff --git a/test/performance-charts/tests/LineChartPro.bench.tsx b/test/performance-charts/tests/LineChartPro.bench.tsx new file mode 100644 index 0000000000000..e3cb93565e98c --- /dev/null +++ b/test/performance-charts/tests/LineChartPro.bench.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports +import { render, cleanup } from '@testing-library/react'; +import { afterEach, bench, describe } from 'vitest'; +import { LineChartPro } from '@mui/x-charts-pro/LineChartPro'; +import { LicenseInfo, generateLicense } from '@mui/x-license'; +import { options } from '../utils/options'; + +describe('LineChartPro', () => { + afterEach(() => { + cleanup(); + }); + + const dataLength = 600; + const data = Array.from({ length: dataLength }).map((_, i) => ({ + x: i, + y: 50 + Math.sin(i / 5) * 25, + })); + + const xData = data.map((d) => d.x); + const yData = data.map((d) => d.y); + + bench( + 'LineChartPro with big data amount', + async () => { + const licenseKey = generateLicense({ + expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), + orderNumber: 'MUI-123', + planScope: 'pro', + licenseModel: 'subscription', + planVersion: 'Q3-2024', + }); + + LicenseInfo.setLicenseKey(licenseKey); + + const { findByText } = render( + , + ); + + await findByText('60', { ignore: 'span' }); + }, + options, + ); +}); diff --git a/test/performance-charts/tests/ScatterChart.bench.tsx b/test/performance-charts/tests/ScatterChart.bench.tsx index 42063af167aec..25fa1cf98ace7 100644 --- a/test/performance-charts/tests/ScatterChart.bench.tsx +++ b/test/performance-charts/tests/ScatterChart.bench.tsx @@ -24,7 +24,7 @@ describe('ScatterChart', () => { async () => { const { findByText } = render( v.toLocaleString('en-US') }]} series={[ { data, @@ -35,7 +35,7 @@ describe('ScatterChart', () => { />, ); - await findByText(dataLength.toLocaleString(), { ignore: 'span' }); + await findByText(dataLength.toLocaleString('en-US'), { ignore: 'span' }); }, options, ); diff --git a/test/performance-charts/tests/ScatterChartPro.bench.tsx b/test/performance-charts/tests/ScatterChartPro.bench.tsx new file mode 100644 index 0000000000000..5ae993349104a --- /dev/null +++ b/test/performance-charts/tests/ScatterChartPro.bench.tsx @@ -0,0 +1,61 @@ +import * as React from 'react'; +// eslint-disable-next-line no-restricted-imports +import { render, cleanup } from '@testing-library/react'; +import { afterEach, bench, describe } from 'vitest'; +import { ScatterChartPro } from '@mui/x-charts-pro/ScatterChartPro'; +import { LicenseInfo, generateLicense } from '@mui/x-license'; +import { options } from '../utils/options'; + +describe('ScatterChartPro', () => { + afterEach(() => { + cleanup(); + }); + + const dataLength = 50; + const data = Array.from({ length: dataLength }).map((_, i) => ({ + id: i, + x: i, + y: 50 + Math.sin(i / 5) * 25, + })); + + const xData = data.map((d) => d.x); + + bench( + 'ScatterChartPro with big data amount', + async () => { + const licenseKey = generateLicense({ + expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), + orderNumber: 'MUI-123', + planScope: 'pro', + licenseModel: 'subscription', + planVersion: 'Q3-2024', + }); + + LicenseInfo.setLicenseKey(licenseKey); + + const { findByText } = render( + v.toLocaleString('en-US'), + }, + ]} + zoom={[{ axisId: 'x', start: 2, end: 7 }]} + series={[ + { + data, + }, + ]} + width={500} + height={300} + />, + ); + + await findByText('60', { ignore: 'span' }); + }, + options, + ); +}); diff --git a/test/performance-charts/utils/options.ts b/test/performance-charts/utils/options.ts index bebd108eb5081..982e45dd89ff3 100644 --- a/test/performance-charts/utils/options.ts +++ b/test/performance-charts/utils/options.ts @@ -1,7 +1,7 @@ import { BenchOptions } from 'vitest'; -const iterations = process.env.BENCHMARK_ITERATIONS - ? parseInt(process.env.BENCHMARK_ITERATIONS, 10) +const iterations = globalThis.process?.env?.BENCHMARK_ITERATIONS + ? parseInt(globalThis.process.env.BENCHMARK_ITERATIONS, 10) : 1; export const options: BenchOptions = { diff --git a/test/performance-charts/vitest.config.ts b/test/performance-charts/vitest.config.ts index d3217a4508bb2..46f3b724801bc 100644 --- a/test/performance-charts/vitest.config.ts +++ b/test/performance-charts/vitest.config.ts @@ -6,12 +6,9 @@ export default defineConfig({ plugins: [codspeedPlugin(), react()], test: { environment: 'jsdom', - // testTimeout: 20000, - // benchmark: { - // outputJson: '../../test-results/benchmark-charts.json', - // }, // browser: { // enabled: true, + // headless: true, // name: 'chromium', // provider: 'playwright', // providerOptions: {