diff --git a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png
index 1e3ca8430d1..465ea69cb5f 100644
Binary files a/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png and b/packages/desktop-client/e2e/mobile.test.js-snapshots/Mobile-loads-the-budget-page-with-budgeted-amounts-1-chromium-linux.png differ
diff --git a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png
index 6648df99dd5..15672c025c7 100644
Binary files a/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png and b/packages/desktop-client/e2e/reports.test.js-snapshots/Reports-loads-net-worth-and-cash-flow-reports-1-chromium-linux.png differ
diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json
index 0036b72eda7..e2b86fe6b52 100644
--- a/packages/desktop-client/package.json
+++ b/packages/desktop-client/package.json
@@ -56,6 +56,7 @@
"react-simple-pull-to-refresh": "^1.3.3",
"react-spring": "^9.7.1",
"react-virtualized-auto-sizer": "^1.0.2",
+ "recharts": "^2.8.0",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"remark-gfm": "^3.0.1",
diff --git a/packages/desktop-client/src/components/reports/Overview.js b/packages/desktop-client/src/components/reports/Overview.js
index d2237ac4704..dbe0f6ba36c 100644
--- a/packages/desktop-client/src/components/reports/Overview.js
+++ b/packages/desktop-client/src/components/reports/Overview.js
@@ -38,6 +38,9 @@ function Card({ flex, to, style, children }) {
height: 200,
boxShadow: '0 2px 6px rgba(0, 0, 0, .15)',
transition: 'box-shadow .25s',
+ '& .recharts-surface:hover': {
+ cursor: 'pointer',
+ },
':hover': to && {
boxShadow: '0 4px 6px rgba(0, 0, 0, .15)',
},
diff --git a/packages/desktop-client/src/components/reports/graphs/NetWorthGraph.tsx b/packages/desktop-client/src/components/reports/graphs/NetWorthGraph.tsx
index ae18bd1f94a..5ef62d35914 100644
--- a/packages/desktop-client/src/components/reports/graphs/NetWorthGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/NetWorthGraph.tsx
@@ -1,21 +1,20 @@
-import React, { createElement } from 'react';
+import React from 'react';
-import * as d from 'date-fns';
+import { css } from 'glamor';
import {
- VictoryChart,
- VictoryBar,
- VictoryArea,
- VictoryAxis,
- VictoryVoronoiContainer,
- VictoryGroup,
-} from 'victory';
+ AreaChart,
+ Area,
+ CartesianGrid,
+ XAxis,
+ YAxis,
+ Tooltip,
+ ResponsiveContainer,
+} from 'recharts';
+import { theme } from '../../../style';
import { type CSSProperties } from '../../../style';
-import { chartTheme } from '../chart-theme';
+import AlignedText from '../../common/AlignedText';
import Container from '../Container';
-import Tooltip from '../Tooltip';
-
-import { Area } from './common';
type NetWorthGraphProps = {
style?: CSSProperties;
@@ -25,13 +24,92 @@ type NetWorthGraphProps = {
y?: [number, number];
};
};
+type PotentialNumber = number | string | undefined | null;
+
+const numberFormatterTooltip = (value: PotentialNumber): number | null => {
+ if (typeof value === 'number') {
+ return Math.round(value);
+ }
+ return null; // or some default value for other cases
+};
+
function NetWorthGraph({
style,
graphData,
compact,
domain,
}: NetWorthGraphProps) {
- const Chart = compact ? VictoryGroup : VictoryChart;
+ const tickFormatter = tick => {
+ return `${Math.round(tick).toLocaleString()}`; // Formats the tick values as strings with commas
+ };
+
+ const gradientOffset = () => {
+ const dataMax = Math.max(...graphData.data.map(i => i.y));
+ const dataMin = Math.min(...graphData.data.map(i => i.y));
+
+ if (dataMax <= 0) {
+ return 0;
+ }
+ if (dataMin >= 0) {
+ return 1;
+ }
+
+ return dataMax / (dataMax - dataMin);
+ };
+
+ const off = gradientOffset();
+
+ type PayloadItem = {
+ payload: {
+ date: string;
+ assets: number | string;
+ debt: number | string;
+ networth: number | string;
+ change: number | string;
+ };
+ };
+
+ type CustomTooltipProps = {
+ active?: boolean;
+ payload?: PayloadItem[];
+ label?: string;
+ };
+
+ const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => {
+ if (active && payload && payload.length) {
+ return (
+
+
+
+ {payload[0].payload.date}
+
+
+
+
+
{payload[0].payload.networth}}
+ />
+
+
+
+
+ );
+ }
+ };
return (
{(width, height, portalHost) =>
graphData && (
-
- }
- padding={
- compact && {
- top: 0,
- bottom: 0,
- left: 0,
- right: 0,
- }
- }
- >
-
- {createElement(
- // @ts-expect-error defaultProps mismatch causing issue
- graphData.data.length === 1 ? VictoryBar : VictoryArea,
- {
- data: graphData.data,
- labelComponent: ,
- labels: x => x.premadeLabel,
- style: {
- data:
- graphData.data.length === 1
- ? { width: 50 }
- : {
- clipPath: 'url(#positive)',
- fill: 'url(#positive-gradient)',
- },
- },
- },
- )}
- {graphData.data.length > 1 && (
-
+
+ {!compact &&
}
+
- )}
- {/* Somehow the path `d` attributes are stripped from second
- `
` above if this is removed. I’m just as
- confused as you are! */}
-
- {!compact && (
-
d.format(x, "MMM ''yy")}
- tickValues={graphData.data.map(item => item.x)}
- tickCount={Math.min(width / 220, graphData.data.length)}
- offsetY={50}
- />
- )}
- {!compact && (
-
- )}
-
+ margin={{ top: 0, right: 0, left: 0, bottom: 0 }}
+ >
+ {compact ? null : (
+
+ )}
+ {compact ? null : }
+ {compact ? null : (
+
+ )}
+ }
+ formatter={numberFormatterTooltip}
+ isAnimationActive={false}
+ />
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/packages/desktop-client/src/components/reports/graphs/net-worth-spreadsheet.tsx b/packages/desktop-client/src/components/reports/graphs/net-worth-spreadsheet.tsx
index 4750d289f64..81a685be8e6 100644
--- a/packages/desktop-client/src/components/reports/graphs/net-worth-spreadsheet.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/net-worth-spreadsheet.tsx
@@ -1,5 +1,3 @@
-import React from 'react';
-
import * as d from 'date-fns';
import q, { runQuery } from 'loot-core/src/client/query-helpers';
@@ -11,7 +9,6 @@ import {
amountToInteger,
} from 'loot-core/src/shared/util';
-import AlignedText from '../../common/AlignedText';
import { index } from '../util';
export default function createSpreadsheet(
@@ -119,29 +116,20 @@ function recalculate(data, start, end) {
const x = d.parseISO(month + '-01');
const change = last ? total - amountToInteger(last.y) : 0;
- const label = (
-
-
- {d.format(x, 'MMMM yyyy')}
-
-
-
-
-
{integerToCurrency(total)}}
- />
-
-
-
- );
-
if (arr.length === 0) {
startNetWorth = total;
}
endNetWorth = total;
- arr.push({ x, y: integerToAmount(total), premadeLabel: label });
+ arr.push({
+ x: d.format(x, 'MMM ’yy'),
+ y: integerToAmount(total),
+ assets: integerToCurrency(assets),
+ debt: `-${integerToCurrency(debt)}`,
+ change: integerToCurrency(change),
+ networth: integerToCurrency(total),
+ date: d.format(x, 'MMMM yyyy'),
+ });
arr.forEach(item => {
if (item.y < lowestNetWorth || lowestNetWorth === null) {
diff --git a/upcoming-release-notes/1740.md b/upcoming-release-notes/1740.md
new file mode 100644
index 00000000000..8a7b1d32e3e
--- /dev/null
+++ b/upcoming-release-notes/1740.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [shaankhosla]
+---
+
+Update the NetWorth graph to use the Recharts library.
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index c68af424224..84e07d99b37 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -102,6 +102,7 @@ __metadata:
react-simple-pull-to-refresh: ^1.3.3
react-spring: ^9.7.1
react-virtualized-auto-sizer: ^1.0.2
+ recharts: ^2.8.0
redux: ^4.0.5
redux-thunk: ^2.3.0
remark-gfm: ^3.0.1
@@ -1680,6 +1681,15 @@ __metadata:
languageName: node
linkType: hard
+"@babel/runtime@npm:^7.1.2":
+ version: 7.23.1
+ resolution: "@babel/runtime@npm:7.23.1"
+ dependencies:
+ regenerator-runtime: ^0.14.0
+ checksum: 0cd0d43e6e7dc7f9152fda8c8312b08321cda2f56ef53d6c22ebdd773abdc6f5d0a69008de90aa41908d00e2c1facb24715ff121274e689305c858355ff02c70
+ languageName: node
+ linkType: hard
+
"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.8, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
version: 7.22.11
resolution: "@babel/runtime@npm:7.22.11"
@@ -6715,6 +6725,13 @@ __metadata:
languageName: node
linkType: hard
+"classnames@npm:^2.2.5":
+ version: 2.3.2
+ resolution: "classnames@npm:2.3.2"
+ checksum: 2c62199789618d95545c872787137262e741f9db13328e216b093eea91c85ef2bfb152c1f9e63027204e2559a006a92eb74147d46c800a9f96297ae1d9f96f4e
+ languageName: node
+ linkType: hard
+
"clean-css@npm:^5.2.2":
version: 5.3.2
resolution: "clean-css@npm:5.3.2"
@@ -7484,6 +7501,13 @@ __metadata:
languageName: node
linkType: hard
+"css-unit-converter@npm:^1.1.1":
+ version: 1.1.2
+ resolution: "css-unit-converter@npm:1.1.2"
+ checksum: 07888033346a5128f34dbe2f72884c966d24e9f29db24416dcde92860242490617ef9a178ac193a92f730834bbeea026cdc7027701d92ba9bbbe59db7a37eb2a
+ languageName: node
+ linkType: hard
+
"css-what@npm:^3.2.1":
version: 3.4.2
resolution: "css-what@npm:3.4.2"
@@ -7822,6 +7846,13 @@ __metadata:
languageName: node
linkType: hard
+"decimal.js-light@npm:^2.4.1":
+ version: 2.5.1
+ resolution: "decimal.js-light@npm:2.5.1"
+ checksum: f5a2c7eac1c4541c8ab8a5c8abea64fc1761cefc7794bd5f8afd57a8a78d1b51785e0c4e4f85f4895a043eaa90ddca1edc3981d1263eb6ddce60f32bf5fe66c9
+ languageName: node
+ linkType: hard
+
"decimal.js@npm:^10.2.1":
version: 10.4.3
resolution: "decimal.js@npm:10.4.3"
@@ -8321,6 +8352,15 @@ __metadata:
languageName: node
linkType: hard
+"dom-helpers@npm:^3.4.0":
+ version: 3.4.0
+ resolution: "dom-helpers@npm:3.4.0"
+ dependencies:
+ "@babel/runtime": ^7.1.2
+ checksum: 58d9f1c4a96daf77eddc63ae1236b826e1cddd6db66bbf39b18d7e21896d99365b376593352d52a60969d67fa4a8dbef26adc1439fa2c1b355efa37cacbaf637
+ languageName: node
+ linkType: hard
+
"dom-serializer@npm:0":
version: 0.2.2
resolution: "dom-serializer@npm:0.2.2"
@@ -9409,7 +9449,7 @@ __metadata:
languageName: node
linkType: hard
-"eventemitter3@npm:^4.0.0":
+"eventemitter3@npm:^4.0.0, eventemitter3@npm:^4.0.1":
version: 4.0.7
resolution: "eventemitter3@npm:4.0.7"
checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374
@@ -9646,6 +9686,13 @@ __metadata:
languageName: node
linkType: hard
+"fast-equals@npm:^5.0.0":
+ version: 5.0.1
+ resolution: "fast-equals@npm:5.0.1"
+ checksum: fbb3b6a74f3a0fa930afac151ff7d01639159b4fddd2678b5d50708e0ba38e9ec14602222d10dadb8398187342692c04fbef5a62b1cfcc7942fe03e754e064bc
+ languageName: node
+ linkType: hard
+
"fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.9":
version: 3.2.12
resolution: "fast-glob@npm:3.2.12"
@@ -16290,6 +16337,13 @@ __metadata:
languageName: node
linkType: hard
+"postcss-value-parser@npm:^3.3.0":
+ version: 3.3.1
+ resolution: "postcss-value-parser@npm:3.3.1"
+ checksum: 62cd26e1cdbcf2dcc6bcedf3d9b409c9027bc57a367ae20d31dd99da4e206f730689471fd70a2abe866332af83f54dc1fa444c589e2381bf7f8054c46209ce16
+ languageName: node
+ linkType: hard
+
"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0":
version: 4.2.0
resolution: "postcss-value-parser@npm:4.2.0"
@@ -16505,7 +16559,7 @@ __metadata:
languageName: node
linkType: hard
-"prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
+"prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
dependencies:
@@ -16790,7 +16844,7 @@ __metadata:
languageName: node
linkType: hard
-"react-is@npm:^16.13.1, react-is@npm:^16.7.0, react-is@npm:^16.9.0":
+"react-is@npm:^16.10.2, react-is@npm:^16.13.1, react-is@npm:^16.7.0, react-is@npm:^16.9.0":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f
@@ -16811,7 +16865,7 @@ __metadata:
languageName: node
linkType: hard
-"react-lifecycles-compat@npm:^3.0.0":
+"react-lifecycles-compat@npm:^3.0.0, react-lifecycles-compat@npm:^3.0.4":
version: 3.0.4
resolution: "react-lifecycles-compat@npm:3.0.4"
checksum: a904b0fc0a8eeb15a148c9feb7bc17cec7ef96e71188280061fc340043fd6d8ee3ff233381f0e8f95c1cf926210b2c4a31f38182c8f35ac55057e453d6df204f
@@ -16901,6 +16955,18 @@ __metadata:
languageName: node
linkType: hard
+"react-resize-detector@npm:^8.0.4":
+ version: 8.1.0
+ resolution: "react-resize-detector@npm:8.1.0"
+ dependencies:
+ lodash: ^4.17.21
+ peerDependencies:
+ react: ^16.0.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
+ checksum: 45e6b87ea7331406bed2a806d0cea98c1467d53a7cfcdf19c2dd55a3460047917d3b175d9cceea6f314b65eb54858cbb981acffd007d67aa16388e517dafb83e
+ languageName: node
+ linkType: hard
+
"react-router-dom@npm:6.11.2":
version: 6.11.2
resolution: "react-router-dom@npm:6.11.2"
@@ -17002,6 +17068,20 @@ __metadata:
languageName: node
linkType: hard
+"react-smooth@npm:^2.0.2":
+ version: 2.0.4
+ resolution: "react-smooth@npm:2.0.4"
+ dependencies:
+ fast-equals: ^5.0.0
+ react-transition-group: 2.9.0
+ peerDependencies:
+ prop-types: ^15.6.0
+ react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
+ checksum: 21731e2f9ebc9594eae0f0d875526185392a87c00abf013c9769ed642a4077b62c04c1001b2527a196aabafb87af208f6c7107db674538c4bb95c253ed123447
+ languageName: node
+ linkType: hard
+
"react-spring@npm:^9.7.1":
version: 9.7.1
resolution: "react-spring@npm:9.7.1"
@@ -17019,6 +17099,21 @@ __metadata:
languageName: node
linkType: hard
+"react-transition-group@npm:2.9.0":
+ version: 2.9.0
+ resolution: "react-transition-group@npm:2.9.0"
+ dependencies:
+ dom-helpers: ^3.4.0
+ loose-envify: ^1.4.0
+ prop-types: ^15.6.2
+ react-lifecycles-compat: ^3.0.4
+ peerDependencies:
+ react: ">=15.0.0"
+ react-dom: ">=15.0.0"
+ checksum: d8c9e50aabdc2cfc324e5cdb0ad1c6eecb02e1c0cd007b26d5b30ccf49015e900683dd489348c71fba4055858308d9ba7019e0d37d0e8d37bd46ed098788f670
+ languageName: node
+ linkType: hard
+
"react-virtualized-auto-sizer@npm:^1.0.2":
version: 1.0.15
resolution: "react-virtualized-auto-sizer@npm:1.0.15"
@@ -17140,6 +17235,36 @@ __metadata:
languageName: node
linkType: hard
+"recharts-scale@npm:^0.4.4":
+ version: 0.4.5
+ resolution: "recharts-scale@npm:0.4.5"
+ dependencies:
+ decimal.js-light: ^2.4.1
+ checksum: e970377190a610e684a32c7461c7684ac3603c2e0ac0020bbba1eea9d099b38138143a8e80bf769bb49c0b7cecf22a2f5c6854885efed2d56f4540d4aa7052bd
+ languageName: node
+ linkType: hard
+
+"recharts@npm:^2.8.0":
+ version: 2.8.0
+ resolution: "recharts@npm:2.8.0"
+ dependencies:
+ classnames: ^2.2.5
+ eventemitter3: ^4.0.1
+ lodash: ^4.17.19
+ react-is: ^16.10.2
+ react-resize-detector: ^8.0.4
+ react-smooth: ^2.0.2
+ recharts-scale: ^0.4.4
+ reduce-css-calc: ^2.1.8
+ victory-vendor: ^36.6.8
+ peerDependencies:
+ prop-types: ^15.6.0
+ react: ^16.0.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
+ checksum: 4638bd5c6c2af8f5c79de5e13cce0e38f06e0bbb0a3c4df27a9b12632fd72c0a0604c8246f55e830f323dfa84a3da7cb2634c2243bb9c775d899fd71f9d4c87a
+ languageName: node
+ linkType: hard
+
"rechoir@npm:^0.8.0":
version: 0.8.0
resolution: "rechoir@npm:0.8.0"
@@ -17158,6 +17283,16 @@ __metadata:
languageName: node
linkType: hard
+"reduce-css-calc@npm:^2.1.8":
+ version: 2.1.8
+ resolution: "reduce-css-calc@npm:2.1.8"
+ dependencies:
+ css-unit-converter: ^1.1.1
+ postcss-value-parser: ^3.3.0
+ checksum: 8fd27c06c4b443b84749a69a8b97d10e6ec7d142b625b41923a8807abb22b9e37e44df14e26cc606a802957be07bdce5e8ee2976a6952a7b438a7727007101e9
+ languageName: node
+ linkType: hard
+
"redux-thunk@npm:^2.3.0":
version: 2.4.2
resolution: "redux-thunk@npm:2.4.2"
@@ -20438,6 +20573,28 @@ __metadata:
languageName: node
linkType: hard
+"victory-vendor@npm:^36.6.8":
+ version: 36.6.11
+ resolution: "victory-vendor@npm:36.6.11"
+ 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
+ checksum: 55800076dfa6abedf7758840986a302778a904678d4b66fe47d977c48b6f9484276b780871e6e5105b31c1eb936e9f1331ee39afcc2869bf65ceb7d456143172
+ languageName: node
+ linkType: hard
+
"victory-voronoi-container@npm:^36.6.10":
version: 36.6.10
resolution: "victory-voronoi-container@npm:36.6.10"