From 3af8979bc553546ccb6afbae8cc5071592b0e6a5 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:13:39 -0700 Subject: [PATCH] chore(deps): migrate recharts to v3 across all three workspaces (#8610) The issue named apps/loopover-ui, but THREE workspaces depended on recharts -- apps/loopover-ui, apps/loopover-miner-ui, and the published @loopover/ui-kit. Bumping only one would have left two majors resolved in the same tree, which is a worse problem than the deprecation warning: two copies bundled, and ui-kit's charts drifting from the app's. Three real v3 breaks, each fixed by reading the library's own types rather than re-stating them locally: - Line's `dot` render prop: v3 widened `key` from string to React's Key, which the hand-written param annotation silently disagreed with. The props are derived from Line's own signature now. - Tooltip/Legend content: v3 moved `payload`/`label` off the components' props (they are read from context) and publishes TooltipContentProps / LegendPayload for the render-prop shape. - A React key taken from `item.dataKey`, which v3 lets be a FUNCTION accessor. The correct string was already computed two lines above. The sparkline had no test at all, which is a poor place to be for a chart-library major: a type-clean upgrade that renders nothing looks exactly like a passing build. It has one now, asserting a line is actually drawn -- verified by mutation, which kills 3 of the 4 cases. --- apps/loopover-miner-ui/package.json | 2 +- apps/loopover-ui/package.json | 2 +- .../src/components/site/sparkline.test.tsx | 75 +++ .../src/components/site/sparkline.tsx | 12 +- package-lock.json | 438 +++++++++++------- packages/loopover-ui-kit/package.json | 2 +- .../loopover-ui-kit/src/components/chart.tsx | 27 +- 7 files changed, 392 insertions(+), 166 deletions(-) create mode 100644 apps/loopover-ui/src/components/site/sparkline.test.tsx diff --git a/apps/loopover-miner-ui/package.json b/apps/loopover-miner-ui/package.json index 8421a42079..d0405a987d 100644 --- a/apps/loopover-miner-ui/package.json +++ b/apps/loopover-miner-ui/package.json @@ -21,7 +21,7 @@ "@tanstack/react-router": "^1.170.18", "react": "^19.2.8", "react-dom": "^19.2.8", - "recharts": "^2.15.4", + "recharts": "^3.10.1", "tailwindcss": "^4.3.3", "tw-animate-css": "^1.4.0", "vite-tsconfig-paths": "^6.1.1" diff --git a/apps/loopover-ui/package.json b/apps/loopover-ui/package.json index e6b3602723..098ce30ec3 100644 --- a/apps/loopover-ui/package.json +++ b/apps/loopover-ui/package.json @@ -73,7 +73,7 @@ "react-dom": "^19.2.8", "react-hook-form": "^7.82.0", "react-resizable-panels": "^4.12.2", - "recharts": "^2.15.4", + "recharts": "^3.10.1", "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.3", diff --git a/apps/loopover-ui/src/components/site/sparkline.test.tsx b/apps/loopover-ui/src/components/site/sparkline.test.tsx new file mode 100644 index 0000000000..8f54d6423c --- /dev/null +++ b/apps/loopover-ui/src/components/site/sparkline.test.tsx @@ -0,0 +1,75 @@ +import { render } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { Sparkline } from "./sparkline"; +import type { TrendPoint } from "./proof-of-power-stats-model"; + +// The sparkline is the one place this app renders a chart library, so a major bump of that library +// (#8610: recharts 2 -> 3) needs a guard that the thing still DRAWS -- a type-clean upgrade that renders +// nothing looks identical to a passing build. +// +// ResponsiveContainer measures its parent, and jsdom reports 0x0 for everything, so the fixed pixel size +// the component already sets is stubbed onto the element's box here. Without it recharts correctly draws +// nothing and every assertion below would pass vacuously against an empty SVG. +function renderSparkline(points: TrendPoint[]) { + const original = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "getBoundingClientRect"); + Object.defineProperty(HTMLElement.prototype, "getBoundingClientRect", { + configurable: true, + value: () => ({ + width: 64, + height: 28, + top: 0, + left: 0, + right: 64, + bottom: 28, + x: 0, + y: 0, + toJSON: () => ({}), + }), + }); + try { + return render(); + } finally { + if (original) Object.defineProperty(HTMLElement.prototype, "getBoundingClientRect", original); + } +} + +const WEEKS: TrendPoint[] = [ + { label: "w1", value: 10 }, + { label: "w2", value: 20 }, + { label: "w3", value: 15 }, +]; + +describe("Sparkline (#8610 recharts v3)", () => { + it("renders an accessible trend image with a drawn line path", () => { + const { container, getByRole } = renderSparkline(WEEKS); + expect(getByRole("img", { name: /trend over the last 3 weeks/i })).toBeTruthy(); + // The actual regression this guards: a v3 API change that type-checks but draws nothing. + const paths = container.querySelectorAll("path.recharts-line-curve, .recharts-line path"); + expect(paths.length).toBeGreaterThan(0); + }); + + it("renders NOTHING when every point is null — a sparkline of no data is not a flat line at zero", () => { + const { container } = renderSparkline([ + { label: "w1", value: null }, + { label: "w2", value: null }, + ]); + expect(container.firstChild).toBeNull(); + }); + + it("still renders when only some weeks are below their sample floor", () => { + // connectNulls={false} is what makes a gap read as "insufficient data" rather than as a straight line + // drawn through it; the component must not bail just because one point is null. + const { getByRole } = renderSparkline([ + { label: "w1", value: 10 }, + { label: "w2", value: null }, + { label: "w3", value: 30 }, + ]); + expect(getByRole("img")).toBeTruthy(); + }); + + it("labels itself with the real number of weeks it was given", () => { + const { getByRole } = renderSparkline([...WEEKS, { label: "w4", value: 12 }]); + expect(getByRole("img", { name: /last 4 weeks/i })).toBeTruthy(); + }); +}); diff --git a/apps/loopover-ui/src/components/site/sparkline.tsx b/apps/loopover-ui/src/components/site/sparkline.tsx index d36004a4f0..25b8237dc3 100644 --- a/apps/loopover-ui/src/components/site/sparkline.tsx +++ b/apps/loopover-ui/src/components/site/sparkline.tsx @@ -1,4 +1,5 @@ import { Line, LineChart, ResponsiveContainer } from "recharts"; +import type { ComponentProps } from "react"; import type { TrendPoint } from "./proof-of-power-stats-model"; @@ -9,6 +10,11 @@ import type { TrendPoint } from "./proof-of-power-stats-model"; // line (a null point, e.g. a week below its own minimum-sample floor) is the correct rendering for "not enough // data yet" -- recharts breaks the segment there rather than drawing a fabricated straight line through it. +/** The props recharts passes to a `dot` render function, taken from `Line`'s own prop type. */ +type LineDotProps = Parameters< + Extract["dot"], (props: never) => unknown> +>[0]; + const SPARKLINE_WIDTH = 64; const SPARKLINE_HEIGHT = 28; @@ -44,7 +50,11 @@ export function Sparkline({ strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" - dot={(props: { index?: number; cx?: number; cy?: number; key?: string }) => { + // The props are DERIVED from Line's own `dot` signature rather than re-typed by hand + // (#8610): recharts v3 widened `key` from `string` to React's `Key | null`, which a + // hand-written annotation silently disagreed with. Reading the library's type means the + // next such change is a compile error here instead of a drifted local copy. + dot={(props: LineDotProps) => { const { index, cx, cy, key } = props; if (index !== lastValueIndex || cx === undefined || cy === undefined) { return ; diff --git a/package-lock.json b/package-lock.json index 149a96b9cf..ee613c0cd9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,7 +76,7 @@ "@tanstack/react-router": "^1.170.18", "react": "^19.2.8", "react-dom": "^19.2.8", - "recharts": "^2.15.4", + "recharts": "^3.10.1", "tailwindcss": "^4.3.3", "tw-animate-css": "^1.4.0", "vite-tsconfig-paths": "^6.1.1" @@ -298,6 +298,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "apps/loopover-miner-ui/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "apps/loopover-miner-ui/node_modules/globals": { "version": "17.8.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.8.0.tgz", @@ -388,6 +394,36 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "apps/loopover-miner-ui/node_modules/recharts": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", + "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "apps/loopover-miner-ui/node_modules/tldts": { "version": "7.4.9", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", @@ -444,6 +480,28 @@ "node": ">=20.18.1" } }, + "apps/loopover-miner-ui/node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "apps/loopover-miner-ui/node_modules/webidl-conversions": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", @@ -534,7 +592,7 @@ "react-dom": "^19.2.8", "react-hook-form": "^7.82.0", "react-resizable-panels": "^4.12.2", - "recharts": "^2.15.4", + "recharts": "^3.10.1", "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "tailwindcss": "^4.3.3", @@ -624,6 +682,12 @@ "undici-types": "~6.21.0" } }, + "apps/loopover-ui/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "apps/loopover-ui/node_modules/lru-cache": { "version": "11.5.2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", @@ -804,6 +868,36 @@ } } }, + "apps/loopover-ui/node_modules/recharts": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", + "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "apps/loopover-ui/node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -811,6 +905,28 @@ "dev": true, "license": "MIT" }, + "apps/loopover-ui/node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/@acemir/cssom": { "version": "0.9.31", "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", @@ -6406,6 +6522,32 @@ "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", "license": "MIT" }, + "node_modules/@reduxjs/toolkit": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz", + "integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, "node_modules/@replit/codemirror-css-color-picker": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@replit/codemirror-css-color-picker/-/codemirror-css-color-picker-6.3.0.tgz", @@ -7557,6 +7699,12 @@ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.23", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", @@ -8886,6 +9034,12 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.21", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", @@ -11814,16 +11968,6 @@ "dev": true, "license": "MIT" }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/drizzle-kit": { "version": "0.31.10", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.10.tgz", @@ -12182,6 +12326,17 @@ "node": ">= 0.4" } }, + "node_modules/es-toolkit": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", + "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types" + ] + }, "node_modules/esast-util-from-estree": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", @@ -12723,12 +12878,6 @@ "integrity": "sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ==", "license": "MIT" }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, "node_modules/events-universal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", @@ -12958,15 +13107,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/fast-equals": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", - "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -14510,6 +14650,16 @@ "node": ">= 4" } }, + "node_modules/immer": { + "version": "11.1.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.15.tgz", + "integrity": "sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-in-the-middle": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.1.tgz", @@ -15405,12 +15555,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -15421,24 +15565,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loose-envify/node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, "node_modules/lovable-tagger": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/lovable-tagger/-/lovable-tagger-1.2.0.tgz", @@ -18121,23 +18247,6 @@ "node": ">=0.4.0" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/property-information": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", @@ -18454,7 +18563,31 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" + "license": "MIT", + "peer": true + }, + "node_modules/react-redux": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz", + "integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } }, "node_modules/react-refresh": { "version": "0.18.0", @@ -18523,21 +18656,6 @@ "react-dom": "^18.0.0 || ^19.0.0" } }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", @@ -18560,22 +18678,6 @@ } } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -18599,39 +18701,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", - "deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, "node_modules/recma-build-jsx": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", @@ -18720,6 +18789,21 @@ "node": ">=4" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", @@ -18977,6 +19061,12 @@ "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, + "node_modules/reselect": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", + "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", + "license": "MIT" + }, "node_modules/reserved-identifiers": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", @@ -20761,28 +20851,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, "node_modules/vite": { "version": "8.1.5", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", @@ -22368,7 +22436,7 @@ "react-day-picker": "^9.14.0", "react-hook-form": "^7.82.0", "react-resizable-panels": "^4.12.2", - "recharts": "^2.15.4", + "recharts": "^3.10.1", "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "vaul": "^1.1.2" @@ -22579,6 +22647,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "packages/loopover-ui-kit/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "packages/loopover-ui-kit/node_modules/html-encoding-sniffer": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", @@ -22656,6 +22730,36 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "packages/loopover-ui-kit/node_modules/recharts": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.10.1.tgz", + "integrity": "sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^11.1.8", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.2.0", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "packages/loopover-ui-kit/node_modules/tldts": { "version": "7.4.9", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.9.tgz", @@ -22719,6 +22823,28 @@ "dev": true, "license": "MIT" }, + "packages/loopover-ui-kit/node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "packages/loopover-ui-kit/node_modules/webidl-conversions": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", diff --git a/packages/loopover-ui-kit/package.json b/packages/loopover-ui-kit/package.json index 1aa308f5ba..740260ffe1 100644 --- a/packages/loopover-ui-kit/package.json +++ b/packages/loopover-ui-kit/package.json @@ -93,7 +93,7 @@ "react-day-picker": "^9.14.0", "react-hook-form": "^7.82.0", "react-resizable-panels": "^4.12.2", - "recharts": "^2.15.4", + "recharts": "^3.10.1", "sonner": "^2.0.7", "tailwind-merge": "^3.6.0", "vaul": "^1.1.2" diff --git a/packages/loopover-ui-kit/src/components/chart.tsx b/packages/loopover-ui-kit/src/components/chart.tsx index 031ccf764d..af28133012 100644 --- a/packages/loopover-ui-kit/src/components/chart.tsx +++ b/packages/loopover-ui-kit/src/components/chart.tsx @@ -102,7 +102,15 @@ const ChartTooltip = RechartsPrimitive.Tooltip; const ChartTooltipContent = React.forwardRef< HTMLDivElement, - React.ComponentProps & + // recharts v3 (#8610) moved `payload`/`label` off Tooltip's own props -- they are read from context + // now -- and publishes the render-prop's shape as TooltipContentProps instead. Reading the library's + // exported type rather than deriving from the component keeps this correct across the next change too. + Partial< + RechartsPrimitive.TooltipContentProps< + RechartsPrimitive.TooltipValueType, + string | number + > + > & React.ComponentProps<"div"> & { hideLabel?: boolean; hideIndicator?: boolean; @@ -192,7 +200,10 @@ const ChartTooltipContent = React.forwardRef< return (
svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", indicator === "dot" && "items-center", @@ -261,10 +272,14 @@ const ChartLegend = RechartsPrimitive.Legend; const ChartLegendContent = React.forwardRef< HTMLDivElement, React.ComponentProps<"div"> & - Pick & { - hideIcon?: boolean; - nameKey?: string; - } + // v3 likewise reads Legend's payload from context; LegendPayload is the published entry shape. + { + payload?: ReadonlyArray; + verticalAlign?: RechartsPrimitive.LegendProps["verticalAlign"]; + } & { + hideIcon?: boolean; + nameKey?: string; + } >( ( { className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },