From fe330ccddb45c8effee1c6f2b842f4abc7ec4ca7 Mon Sep 17 00:00:00 2001 From: Mads Nedergaard Date: Tue, 6 Dec 2022 21:14:22 +0100 Subject: [PATCH] Bar Breakdown Chart (#65) Co-authored-by: Markus Killendahl --- web/pnpm-lock.yaml | 307 +++++++++--------- .../bar-breakdown/BarBreakdownChart.tsx | 75 +++++ .../BarBreakdownEmissionsChart.tsx | 122 +++++++ .../BarBreakdownProductionChart.tsx | 163 ++++++++++ .../charts/bar-breakdown/constants.ts | 9 + .../charts/bar-breakdown/elements/Axis.tsx | 41 +++ .../bar-breakdown/elements/HorizontalBar.tsx | 37 +++ .../charts/bar-breakdown/elements/Row.tsx | 75 +++++ .../charts/bar-breakdown/utils.test.ts | 224 +++++++++++++ .../features/charts/bar-breakdown/utils.ts | 187 +++++++++++ .../useBarBreakdownProductionChartData.ts | 64 ++++ web/src/features/panels/LeftPanel.tsx | 2 +- .../panels/ranking-panel/RankingPanel.tsx | 6 +- .../ranking-panel/getRankingPanelData.ts | 2 - web/src/features/panels/zone/ZoneDetails.tsx | 2 + .../BarBreakdownEmissionsChart.stories.ts | 202 ++++++++++++ .../BarBreakdownProductionChart.stories.ts | 202 ++++++++++++ web/src/types.ts | 81 +++-- web/src/utils/constants.ts | 2 +- 19 files changed, 1625 insertions(+), 178 deletions(-) create mode 100644 web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx create mode 100644 web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx create mode 100644 web/src/features/charts/bar-breakdown/BarBreakdownProductionChart.tsx create mode 100644 web/src/features/charts/bar-breakdown/constants.ts create mode 100644 web/src/features/charts/bar-breakdown/elements/Axis.tsx create mode 100644 web/src/features/charts/bar-breakdown/elements/HorizontalBar.tsx create mode 100644 web/src/features/charts/bar-breakdown/elements/Row.tsx create mode 100644 web/src/features/charts/bar-breakdown/utils.test.ts create mode 100644 web/src/features/charts/bar-breakdown/utils.ts create mode 100644 web/src/features/charts/hooks/useBarBreakdownProductionChartData.ts create mode 100644 web/src/stories/charts/BarBreakdownEmissionsChart.stories.ts create mode 100644 web/src/stories/charts/BarBreakdownProductionChart.stories.ts diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index db3bd0607b..44f6b410aa 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -383,7 +383,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-compilation-targets/7.19.0_@babel+core@7.18.10: @@ -425,6 +425,19 @@ packages: semver: 6.3.0 dev: true + /@babel/helper-compilation-targets/7.20.0_@babel+core@7.18.10: + resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.20.1 + '@babel/core': 7.18.10 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.2: resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} engines: {node: '>=6.9.0'} @@ -531,8 +544,8 @@ packages: '@babel/core': ^7.4.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-compilation-targets': 7.19.0_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.18.10 + '@babel/helper-plugin-utils': 7.20.2 debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.1 @@ -566,7 +579,7 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-function-name/7.19.0: @@ -574,21 +587,21 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.18.10 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-hoist-variables/7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-member-expression-to-functions/7.18.9: resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-module-imports/7.18.6: @@ -634,7 +647,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-plugin-utils/7.18.9: @@ -657,7 +670,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.18.11 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -672,7 +685,7 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.18.11 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -684,8 +697,8 @@ packages: '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/traverse': 7.19.0 - '@babel/types': 7.19.0 + '@babel/traverse': 7.20.1 + '@babel/types': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -707,7 +720,7 @@ packages: resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-simple-access/7.20.2: @@ -721,14 +734,14 @@ packages: resolution: {integrity: sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-split-export-declaration/7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/helper-string-parser/7.18.10: @@ -757,8 +770,8 @@ packages: dependencies: '@babel/helper-function-name': 7.19.0 '@babel/template': 7.18.10 - '@babel/traverse': 7.19.0 - '@babel/types': 7.19.0 + '@babel/traverse': 7.20.1 + '@babel/types': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -817,7 +830,7 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.18.6_@babel+core@7.20.2: @@ -827,7 +840,7 @@ packages: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.18.9_@babel+core@7.18.10: @@ -837,7 +850,7 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.18.10 dev: true @@ -849,7 +862,7 @@ packages: '@babel/core': ^7.13.0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.20.2 dev: true @@ -862,7 +875,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.18.10 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.18.10 transitivePeerDependencies: @@ -892,7 +905,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -905,7 +918,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -918,7 +931,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.18.10 transitivePeerDependencies: - supports-color @@ -932,7 +945,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.20.2 transitivePeerDependencies: - supports-color @@ -945,7 +958,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.18.10 dev: true @@ -956,7 +969,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.20.2 dev: true @@ -967,7 +980,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.18.10 dev: true @@ -978,7 +991,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.20.2 dev: true @@ -989,7 +1002,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.18.10 dev: true @@ -1000,7 +1013,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.20.2 dev: true @@ -1011,7 +1024,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.18.10 dev: true @@ -1022,7 +1035,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.20.2 dev: true @@ -1033,7 +1046,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.18.10 dev: true @@ -1044,7 +1057,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.20.2 dev: true @@ -1055,7 +1068,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.18.10 dev: true @@ -1066,7 +1079,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.20.2 dev: true @@ -1079,7 +1092,7 @@ packages: '@babel/compat-data': 7.19.0 '@babel/core': 7.18.10 '@babel/helper-compilation-targets': 7.19.0_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.18.10 '@babel/plugin-transform-parameters': 7.18.8_@babel+core@7.18.10 dev: true @@ -1105,7 +1118,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.10 dev: true @@ -1116,7 +1129,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.20.2 dev: true @@ -1127,7 +1140,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.10 dev: true @@ -1139,7 +1152,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.20.2 dev: true @@ -1152,7 +1165,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -1165,7 +1178,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -1179,7 +1192,7 @@ packages: '@babel/core': 7.18.10 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.18.10 transitivePeerDependencies: - supports-color @@ -1194,7 +1207,7 @@ packages: '@babel/core': 7.20.2 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.20.2 transitivePeerDependencies: - supports-color @@ -1208,7 +1221,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-proposal-unicode-property-regex/7.18.6_@babel+core@7.20.2: @@ -1219,7 +1232,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.18.10: @@ -1228,7 +1241,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.20.2: @@ -1237,7 +1250,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.18.10: @@ -1246,7 +1259,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.20.2: @@ -1255,7 +1268,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.18.10: @@ -1265,7 +1278,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-class-static-block/7.14.5_@babel+core@7.20.2: @@ -1275,7 +1288,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.18.10: @@ -1284,7 +1297,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.20.2: @@ -1293,7 +1306,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.18.10: @@ -1302,7 +1315,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.20.2: @@ -1311,7 +1324,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-flow/7.18.6_@babel+core@7.20.2: @@ -1331,7 +1344,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-import-assertions/7.20.0_@babel+core@7.20.2: @@ -1350,7 +1363,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.20.2: @@ -1359,7 +1372,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-jsx/7.18.6: @@ -1387,7 +1400,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.20.2: @@ -1396,7 +1409,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.18.10: @@ -1405,7 +1418,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.20.2: @@ -1414,7 +1427,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.18.10: @@ -1423,7 +1436,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.20.2: @@ -1432,7 +1445,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.18.10: @@ -1441,7 +1454,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.20.2: @@ -1450,7 +1463,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.18.10: @@ -1459,7 +1472,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.20.2: @@ -1468,7 +1481,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.18.10: @@ -1477,7 +1490,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.20.2: @@ -1486,7 +1499,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.18.10: @@ -1496,7 +1509,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.20.2: @@ -1506,7 +1519,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.18.10: @@ -1516,7 +1529,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.20.2: @@ -1526,7 +1539,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-typescript/7.20.0_@babel+core@7.20.2: @@ -1546,7 +1559,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-arrow-functions/7.18.6_@babel+core@7.20.2: @@ -1556,7 +1569,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-async-to-generator/7.18.6_@babel+core@7.18.10: @@ -1567,7 +1580,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-module-imports': 7.18.6 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.18.10 transitivePeerDependencies: - supports-color @@ -1581,7 +1594,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-module-imports': 7.18.6 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.20.2 transitivePeerDependencies: - supports-color @@ -1594,7 +1607,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-block-scoped-functions/7.18.6_@babel+core@7.20.2: @@ -1604,7 +1617,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-block-scoping/7.18.9_@babel+core@7.18.10: @@ -1614,7 +1627,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-block-scoping/7.20.2_@babel+core@7.20.2: @@ -1638,7 +1651,7 @@ packages: '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.19.0 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-replace-supers': 7.18.9 '@babel/helper-split-export-declaration': 7.18.6 globals: 11.12.0 @@ -1673,7 +1686,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-computed-properties/7.18.9_@babel+core@7.20.2: @@ -1683,7 +1696,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-destructuring/7.18.9_@babel+core@7.18.10: @@ -1693,7 +1706,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-destructuring/7.20.2_@babel+core@7.20.2: @@ -1714,7 +1727,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-dotall-regex/7.18.6_@babel+core@7.20.2: @@ -1725,7 +1738,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.18.10: @@ -1735,7 +1748,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-duplicate-keys/7.18.9_@babel+core@7.20.2: @@ -1745,7 +1758,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.18.10: @@ -1756,7 +1769,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-exponentiation-operator/7.18.6_@babel+core@7.20.2: @@ -1767,7 +1780,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-flow-strip-types/7.19.0_@babel+core@7.20.2: @@ -1788,7 +1801,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-for-of/7.18.8_@babel+core@7.20.2: @@ -1798,7 +1811,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.18.10: @@ -1810,7 +1823,7 @@ packages: '@babel/core': 7.18.10 '@babel/helper-compilation-targets': 7.19.0_@babel+core@7.18.10 '@babel/helper-function-name': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-function-name/7.18.9_@babel+core@7.20.2: @@ -1822,7 +1835,7 @@ packages: '@babel/core': 7.20.2 '@babel/helper-compilation-targets': 7.19.0_@babel+core@7.20.2 '@babel/helper-function-name': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-literals/7.18.9_@babel+core@7.18.10: @@ -1832,7 +1845,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-literals/7.18.9_@babel+core@7.20.2: @@ -1842,7 +1855,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.18.10: @@ -1852,7 +1865,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-member-expression-literals/7.18.6_@babel+core@7.20.2: @@ -1862,7 +1875,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-modules-amd/7.18.6_@babel+core@7.18.10: @@ -1873,7 +1886,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-module-transforms': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color @@ -1900,7 +1913,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-module-transforms': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-simple-access': 7.18.6 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: @@ -1930,7 +1943,7 @@ packages: '@babel/core': 7.18.10 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-module-transforms': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-identifier': 7.19.1 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: @@ -1960,7 +1973,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-module-transforms': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -1973,7 +1986,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-module-transforms': 7.19.0 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true @@ -1986,7 +1999,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-named-capturing-groups-regex/7.19.1_@babel+core@7.20.2: @@ -2007,7 +2020,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-new-target/7.18.6_@babel+core@7.20.2: @@ -2017,7 +2030,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-object-super/7.18.6_@babel+core@7.18.10: @@ -2027,7 +2040,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-replace-supers': 7.18.9 transitivePeerDependencies: - supports-color @@ -2040,7 +2053,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-replace-supers': 7.18.9 transitivePeerDependencies: - supports-color @@ -2053,7 +2066,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-parameters/7.20.3_@babel+core@7.20.2: @@ -2073,7 +2086,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-property-literals/7.18.6_@babel+core@7.20.2: @@ -2083,7 +2096,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.19.0: @@ -2153,7 +2166,7 @@ packages: '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-syntax-jsx': 7.18.6 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 dev: true /@babel/plugin-transform-regenerator/7.18.6_@babel+core@7.18.10: @@ -2163,7 +2176,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 regenerator-transform: 0.15.0 dev: true @@ -2174,7 +2187,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 regenerator-transform: 0.15.0 dev: true @@ -2185,7 +2198,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-reserved-words/7.18.6_@babel+core@7.20.2: @@ -2195,7 +2208,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.18.10: @@ -2205,7 +2218,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-shorthand-properties/7.18.6_@babel+core@7.20.2: @@ -2215,7 +2228,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-spread/7.18.9_@babel+core@7.18.10: @@ -2225,7 +2238,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 dev: true @@ -2247,7 +2260,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-sticky-regex/7.18.6_@babel+core@7.20.2: @@ -2257,7 +2270,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.18.10: @@ -2267,7 +2280,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-template-literals/7.18.9_@babel+core@7.20.2: @@ -2277,7 +2290,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.18.10: @@ -2287,7 +2300,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-typeof-symbol/7.18.9_@babel+core@7.20.2: @@ -2297,7 +2310,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-typescript/7.20.2_@babel+core@7.20.2: @@ -2321,7 +2334,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-unicode-escapes/7.18.10_@babel+core@7.20.2: @@ -2331,7 +2344,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.18.10: @@ -2342,7 +2355,7 @@ packages: dependencies: '@babel/core': 7.18.10 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-transform-unicode-regex/7.18.6_@babel+core@7.20.2: @@ -2353,7 +2366,7 @@ packages: dependencies: '@babel/core': 7.20.2 '@babel/helper-create-regexp-features-plugin': 7.18.6_@babel+core@7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/preset-env/7.18.10_@babel+core@7.18.10: @@ -2546,10 +2559,10 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.18.10 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.18.10 '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.18.10 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 esutils: 2.0.3 dev: true @@ -2559,10 +2572,10 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.2 - '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.20.2 '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.20.2 - '@babel/types': 7.19.0 + '@babel/types': 7.20.2 esutils: 2.0.3 dev: true @@ -3905,7 +3918,7 @@ packages: '@storybook/csf-plugin': 7.0.0-alpha.47 '@storybook/csf-tools': 7.0.0-alpha.47 '@storybook/docs-tools': 7.0.0-alpha.47_yalvw3r2waubxycyb7k7qsruca - '@storybook/mdx2-csf': 0.1.0-next.6 + '@storybook/mdx2-csf': 0.1.0-next.7 '@storybook/node-logger': 7.0.0-alpha.47 '@storybook/postinstall': 7.0.0-alpha.47 '@storybook/preview-web': 7.0.0-alpha.47_biqbaboplfbrettd7655fr4n2y @@ -3944,7 +3957,7 @@ packages: '@storybook/components': 7.0.0-alpha.54_biqbaboplfbrettd7655fr4n2y '@storybook/csf-plugin': 7.0.0-alpha.54 '@storybook/csf-tools': 7.0.0-alpha.54 - '@storybook/mdx2-csf': 0.1.0-next.6 + '@storybook/mdx2-csf': 0.1.0-next.7 '@storybook/node-logger': 7.0.0-alpha.54 '@storybook/postinstall': 7.0.0-alpha.54 '@storybook/preview-api': 7.0.0-alpha.54 @@ -4451,7 +4464,7 @@ packages: '@storybook/client-api': 7.0.0-alpha.54 '@storybook/client-logger': 7.0.0-alpha.54 '@storybook/core-common': 7.0.0-alpha.54_yalvw3r2waubxycyb7k7qsruca - '@storybook/mdx2-csf': 0.1.0-next.6 + '@storybook/mdx2-csf': 0.1.0-next.7 '@storybook/node-logger': 7.0.0-alpha.54 '@storybook/preview': 7.0.0-alpha.54 '@storybook/preview-api': 7.0.0-alpha.54 @@ -4971,8 +4984,8 @@ packages: resolution: {integrity: sha512-lMRKO083NQSSvO/FgOETzn0sf6eZ5YPirvp1+KD+sWkwzPTib8p+g14fbbH/88MV1GpMaXIHP/FD/pEc1jf4Cw==} dev: true - /@storybook/mdx2-csf/0.1.0-next.6: - resolution: {integrity: sha512-FDBPTDHG9BcI27Zp5kwkqjsv2A3s8aUahtKeHOsmTicOBS8uWqUZ3jmL7vg6N7SpiUUBAft9/vTQfttNLeHO7w==} + /@storybook/mdx2-csf/0.1.0-next.7: + resolution: {integrity: sha512-ahwV2A4aS/D79WWum8fmrFhhbC28/f4XlEKtNG1s0EkCVwCSU17bthEBRY5/Y3IBR5jfeN/oApizNQMLh7mqAA==} dependencies: loader-utils: 2.0.4 dev: true @@ -5311,7 +5324,7 @@ packages: /@storybook/types/7.0.0-alpha.54: resolution: {integrity: sha512-HARhRImgXftuvbnG3wBzhKrNYk6IXMQVsQnadPOxOoJQcIr2V0H8+f5dqrOjR7J6uzhD7d2pWWk1DGdq3o0Jww==} dependencies: - '@babel/core': 7.20.2 + '@babel/core': 7.19.0 '@storybook/channels': 7.0.0-alpha.54 '@types/babel__core': 7.1.20 '@types/express': 4.17.14 @@ -10287,8 +10300,8 @@ packages: resolution: {integrity: sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg==} engines: {node: '>=8.3.0'} dependencies: - '@babel/traverse': 7.20.1 - '@babel/types': 7.20.2 + '@babel/traverse': 7.19.0 + '@babel/types': 7.19.0 c8: 7.12.0 transitivePeerDependencies: - supports-color @@ -13928,7 +13941,7 @@ packages: engines: {node: '>=12.0.0'} hasBin: true dependencies: - '@babel/core': 7.20.2 + '@babel/core': 7.19.0 '@babel/generator': 7.19.0 ast-types: 0.14.2 commander: 2.20.3 diff --git a/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx b/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx new file mode 100644 index 0000000000..044d5fb7c2 --- /dev/null +++ b/web/src/features/charts/bar-breakdown/BarBreakdownChart.tsx @@ -0,0 +1,75 @@ +import { useAtom } from 'jotai'; +import { PulseLoader } from 'react-spinners'; +import { useTranslation } from 'translation/translation'; +import { TimeAverages } from 'utils/constants'; +import { displayByEmissionsAtom } from 'utils/state/atoms'; +import { useRefWidthHeightObserver } from 'utils/viewport'; +import useBarBreakdownChartData from '../hooks/useBarBreakdownProductionChartData'; +import BarBreakdownEmissionsChart from './BarBreakdownEmissionsChart'; +import BarBreakdownProductionChart from './BarBreakdownProductionChart'; + +function BarBreakdownChart({ timeAverage }: { timeAverage: TimeAverages }) { + const { data, productionData, exchangeData, isLoading, height } = + useBarBreakdownChartData(); + const [displayByEmissions] = useAtom(displayByEmissionsAtom); + const { ref, width } = useRefWidthHeightObserver(); + const { __ } = useTranslation(); + + if (isLoading || !data) { + // TODO: Replace with skeleton graph (maybe full graph with no data?) + return ; + } + + // TODO: Show CountryTableOverlayIfNoData when required + + const todoHandler = () => { + console.warn('TODO: Handle tooltips'); + // see countrytable.jsx + // handleProductionRowMouseOver + //handleProductionRowMouseOut + //handleExchangeRowMouseOver + //handleExchangeRowMouseOut + }; + + return ( +
+
+ {__( + timeAverage !== TimeAverages.HOURLY + ? 'country-panel.averagebysource' + : 'country-panel.bysource' + )} +
+ + {displayByEmissions ? ( + + ) : ( + + )} +
+ ); +} + +export default BarBreakdownChart; diff --git a/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx b/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx new file mode 100644 index 0000000000..bcb1504fe4 --- /dev/null +++ b/web/src/features/charts/bar-breakdown/BarBreakdownEmissionsChart.tsx @@ -0,0 +1,122 @@ +import { CountryFlag } from 'components/Flag'; +import { max as d3Max } from 'd3-array'; + +import { scaleLinear } from 'd3-scale'; +import { useMemo } from 'react'; +import { useTranslation } from 'translation/translation'; +import { ZoneDetail } from 'types'; +import { modeColor } from 'utils/constants'; +import { LABEL_MAX_WIDTH, PADDING_X } from './constants'; +import Axis from './elements/Axis'; +import HorizontalBar from './elements/HorizontalBar'; +import Row from './elements/Row'; +import { ExchangeDataType, getDataBlockPositions, ProductionDataType } from './utils'; + +interface BarBreakdownEmissionsChartProps { + height: number; + width: number; + data: ZoneDetail; + exchangeData: ExchangeDataType[]; + productionData: ProductionDataType[]; + isMobile: boolean; + onProductionRowMouseOver: (data: ZoneDetail) => void; + onProductionRowMouseOut: () => void; + onExchangeRowMouseOver: (data: ZoneDetail) => void; + onExchangeRowMouseOut: () => void; +} + +function BarBreakdownEmissionsChart({ + data, + exchangeData, + height, + isMobile, + productionData, + onProductionRowMouseOver, + onProductionRowMouseOut, + onExchangeRowMouseOver, + onExchangeRowMouseOut, + width, +}: BarBreakdownEmissionsChartProps) { + const { __ } = useTranslation(); + const { productionY, exchangeY } = getDataBlockPositions( + productionData.length || 0, + exchangeData + ); + + const maxCO2eqExport = d3Max(exchangeData, (d) => Math.max(0, -d.tCo2eqPerMin)) || 0; + const maxCO2eqImport = d3Max(exchangeData, (d) => Math.max(0, d.tCo2eqPerMin)); + const maxCO2eqProduction = d3Max(productionData, (d) => d.tCo2eqPerMin); + + // in tCO₂eq/min + const co2Scale = useMemo( + () => + scaleLinear() + .domain([ + -maxCO2eqExport || 0, + Math.max(maxCO2eqProduction || 0, maxCO2eqImport || 0), + ]) + .range([0, width - LABEL_MAX_WIDTH - PADDING_X]), + [maxCO2eqExport, maxCO2eqProduction, maxCO2eqImport, width] + ); + + const formatTick = (t: number) => { + const [x1, x2] = co2Scale.domain(); + if (x2 - x1 <= 1) { + return `${t * 1e3} kg/min`; + } + return `${t} t/min`; + }; + + return ( + + + + {productionData.map((d, index) => ( + onProductionRowMouseOver(d.mode, data, event)} + onMouseOut={onProductionRowMouseOut} + isMobile={isMobile} + > + + + ))} + + + {exchangeData.map((d, index) => ( + onExchangeRowMouseOver(d.mode, data, event)} + onMouseOut={onExchangeRowMouseOut} + isMobile={isMobile} + > + + + + ))} + + + ); +} + +export default BarBreakdownEmissionsChart; diff --git a/web/src/features/charts/bar-breakdown/BarBreakdownProductionChart.tsx b/web/src/features/charts/bar-breakdown/BarBreakdownProductionChart.tsx new file mode 100644 index 0000000000..e7a508f06e --- /dev/null +++ b/web/src/features/charts/bar-breakdown/BarBreakdownProductionChart.tsx @@ -0,0 +1,163 @@ +import { CountryFlag } from 'components/Flag'; +import { max as d3Max, min as d3Min } from 'd3-array'; + +import { scaleLinear } from 'd3-scale'; +import { useCo2ColorScale } from 'hooks/theme'; +import { useMemo } from 'react'; +import { useTranslation } from 'translation/translation'; +import { ZoneDetail } from 'types'; +import { modeColor } from 'utils/constants'; +import { LABEL_MAX_WIDTH, PADDING_X } from './constants'; +import Axis from './elements/Axis'; +import HorizontalBar from './elements/HorizontalBar'; +import Row from './elements/Row'; +import { + ExchangeDataType, + getDataBlockPositions, + getElectricityProductionValue, + ProductionDataType, +} from './utils'; + +interface BarBreakdownProductionChartProps { + height: number; + width: number; + data: ZoneDetail; + exchangeData: ExchangeDataType[]; + productionData: ProductionDataType[]; + isMobile: boolean; + onProductionRowMouseOver: (data: ZoneDetail) => void; + onProductionRowMouseOut: () => void; + onExchangeRowMouseOver: (data: ZoneDetail) => void; + onExchangeRowMouseOut: () => void; +} + +function BarBreakdownProductionChart({ + data, + exchangeData, + height, + isMobile, + productionData, + onProductionRowMouseOver, + onProductionRowMouseOut, + onExchangeRowMouseOver, + onExchangeRowMouseOut, + width, +}: BarBreakdownProductionChartProps) { + const { __ } = useTranslation(); + const co2ColorScale = useCo2ColorScale(); + const { productionY, exchangeY } = getDataBlockPositions( + productionData.length, + exchangeData + ); + + // Use the whole history to determine the min/max values in order to avoid + // graph jumping while sliding through the time range. + const [minPower, maxPower] = useMemo(() => { + return [ + d3Min( + Object.values(data.zoneStates).map((zoneData) => + Math.min( + -zoneData.maxStorageCapacity || 0, + -zoneData.maxStorage || 0, + -zoneData.maxExport || 0, + -zoneData.maxExportCapacity || 0 + ) + ) + ) || 0, + d3Max( + Object.values(data.zoneStates).map((zoneData) => + Math.max( + zoneData.maxCapacity || 0, + zoneData.maxProduction || 0, + zoneData.maxDischarge || 0, + zoneData.maxStorageCapacity || 0, + zoneData.maxImport || 0, + zoneData.maxImportCapacity || 0 + ) + ) + ) || 0, + ]; + }, [data]); + + // Power in MW + const powerScale = scaleLinear() + .domain([minPower, maxPower]) + .range([0, width - LABEL_MAX_WIDTH - PADDING_X]); + + const formatTick = (t: number) => { + const [x1, x2] = powerScale.domain(); + if (x2 - x1 <= 1) { + return `${t * 1e3} kW`; + } + if (x2 - x1 <= 1e3) { + return `${t} MW`; + } + return `${t * 1e-3} GW`; + }; + + return ( + + + + {productionData.map((d, index) => ( + onProductionRowMouseOver(d.mode, data, event)} + onMouseOut={onProductionRowMouseOut} + isMobile={isMobile} + > + + + + ))} + + + {exchangeData.map((d, index) => ( + onExchangeRowMouseOver(d.mode, data, event)} + onMouseOut={onExchangeRowMouseOut} + isMobile={isMobile} + > + + + + + + ))} + + + ); +} + +export default BarBreakdownProductionChart; diff --git a/web/src/features/charts/bar-breakdown/constants.ts b/web/src/features/charts/bar-breakdown/constants.ts new file mode 100644 index 0000000000..92bea3d44c --- /dev/null +++ b/web/src/features/charts/bar-breakdown/constants.ts @@ -0,0 +1,9 @@ +export const LABEL_MAX_WIDTH = 102; +export const TEXT_ADJUST_Y = 11; +export const ROW_HEIGHT = 13; +export const PADDING_Y = 7; +export const PADDING_X = 5; +export const RECT_OPACITY = 0.8; +export const X_AXIS_HEIGHT = 15; +export const DEFAULT_FLAG_SIZE = 16; +export const SCALE_TICKS = 4; diff --git a/web/src/features/charts/bar-breakdown/elements/Axis.tsx b/web/src/features/charts/bar-breakdown/elements/Axis.tsx new file mode 100644 index 0000000000..5596cc7411 --- /dev/null +++ b/web/src/features/charts/bar-breakdown/elements/Axis.tsx @@ -0,0 +1,41 @@ +import { ScaleLinear } from 'd3-scale'; +import { LABEL_MAX_WIDTH, SCALE_TICKS, X_AXIS_HEIGHT } from '../constants'; + +type Props = { + height: number; + scale: ScaleLinear; + formatTick: (tick: number) => string; +}; + +export default function Axis({ formatTick, height, scale }: Props) { + return ( + + + {scale.ticks(SCALE_TICKS).map((t) => ( + + + + {formatTick(t)} + + + ))} + + ); +} diff --git a/web/src/features/charts/bar-breakdown/elements/HorizontalBar.tsx b/web/src/features/charts/bar-breakdown/elements/HorizontalBar.tsx new file mode 100644 index 0000000000..8f49f595c5 --- /dev/null +++ b/web/src/features/charts/bar-breakdown/elements/HorizontalBar.tsx @@ -0,0 +1,37 @@ +import { ScaleLinear } from 'd3-scale'; +import { LABEL_MAX_WIDTH, RECT_OPACITY, ROW_HEIGHT } from '../constants'; + +type Props = { + className: string; + fill: string; + range: [number, number]; + scale: ScaleLinear; +}; + +export default function HorizontalBar({ className, fill, range, scale }: Props) { + // Don't render if the range is not valid + if (!Array.isArray(range) || !Number.isFinite(range[0]) || !Number.isFinite(range[1])) { + return null; + } + + const x1 = Math.min(range[0], range[1]); + const x2 = Math.max(range[0], range[1]); + const width = scale(x2) - scale(x1); + + // Don't render if the width is not positive + if (width <= 0) { + return null; + } + + return ( + + ); +} diff --git a/web/src/features/charts/bar-breakdown/elements/Row.tsx b/web/src/features/charts/bar-breakdown/elements/Row.tsx new file mode 100644 index 0000000000..ced9eb94dd --- /dev/null +++ b/web/src/features/charts/bar-breakdown/elements/Row.tsx @@ -0,0 +1,75 @@ +import { ScaleLinear } from 'd3-scale'; +import { MouseEventHandler } from 'react'; +import { LABEL_MAX_WIDTH, PADDING_Y, ROW_HEIGHT, TEXT_ADJUST_Y } from '../constants'; + +type Props = { + children: React.ReactNode; + index: number; + isMobile: boolean; + label: string; + scale: ScaleLinear; + value: number; + onMouseOver: MouseEventHandler; + onMouseOut: () => void; + width: number; +}; + +export default function Row({ + children, + index, + isMobile, + label, + scale, + value, + onMouseOver, + onMouseOut, + width, +}: Props) { + // Don't render if the width is not positive + if (width <= 0) { + return null; + } + + return ( + + {/* Row background */} + {}} + onFocus={!isMobile ? onMouseOver : () => {}} + onMouseOver={!isMobile ? onMouseOver : () => {}} + onMouseMove={!isMobile ? onMouseOver : () => {}} + onMouseOut={onMouseOut} + onBlur={onMouseOut} + /> + + {/* Row label */} + + {label} + + + {/* Row content */} + {children} + + {/* Question mark if the value is not defined */} + {!Number.isFinite(value) && ( + + ? + + )} + + ); +} diff --git a/web/src/features/charts/bar-breakdown/utils.test.ts b/web/src/features/charts/bar-breakdown/utils.test.ts new file mode 100644 index 0000000000..222f95847b --- /dev/null +++ b/web/src/features/charts/bar-breakdown/utils.test.ts @@ -0,0 +1,224 @@ +import { getDataBlockPositions, getProductionData } from './utils'; + +const zoneDetailsData = { + co2intensity: 187.32, + co2intensityProduction: 190.6, + countryCode: 'PT', + fossilFuelRatio: 0.3, + fossilFuelRatioProduction: 0.3, + renewableRatio: 0.7, + renewableRatioProduction: 0.7, + stateDatetime: '2022-11-28T07:00:00.000Z', + _isFinestGranularity: true, + capacity: { + 'battery storage': null, + biomass: 700, + coal: 0, + gas: 4520, + geothermal: 0, + hydro: 4578, + 'hydro storage': 3585, + nuclear: 0, + oil: 0, + solar: 1616, + unknown: null, + wind: 5389, + }, + dischargeCo2Intensities: { + battery: 136.442_667_362_438_53, + hydro: 136.442_667_362_438_53, + }, + dischargeCo2IntensitySources: { + battery: 'electricityMap, 2021 average', + hydro: 'electricityMap, 2021 average', + }, + exchange: { ES: -934 }, + exchangeCapacities: {}, + exchangeCo2Intensities: { ES: 187.32 }, + isValid: true, + maxCapacity: 5389, + maxDischarge: 395, + maxExport: 934, + maxImport: 0, + maxProduction: 2365, + maxStorage: 0, + maxStorageCapacity: 3585, + price: { currency: 'EUR', value: 120 }, + production: { + biomass: 350, + coal: 0, + gas: 1930, + geothermal: null, + hydro: 1445, + nuclear: null, + oil: null, + solar: 17, + unknown: 29, + wind: 2365, + }, + productionCo2Intensities: { + biomass: 439.145_806, + coal: 1099.976_527, + gas: 492.109_361, + geothermal: 38, + hydro: 10.7, + nuclear: 5.13, + oil: 1124.903_938, + solar: 25.6, + unknown: 700, + wind: 12.62, + }, + productionCo2IntensitySources: { + biomass: 'EU-ETS, ENTSO-E 2021', + coal: 'eGrid 2020; Oberschelp, Christopher, et al. "Global emission hotspots of coal power generation."', + gas: 'IPCC 2014; EU-ETS, ENTSO-E 2021', + geothermal: 'IPCC 2014', + hydro: 'UNECE 2022', + nuclear: 'UNECE 2022', + oil: 'IPCC 2014; EU-ETS, ENTSO-E 2021', + solar: 'INCER ACV', + unknown: 'assumes thermal (coal, gas, oil or biomass)', + wind: 'UNECE 2022, WindEurope "Wind energy in Europe, 2021 Statistics and the outlook for 2022-2026" Wind Europe Proceedings (2021)', + }, + source: ['entsoe.eu'], + storage: { battery: null, hydro: -395 }, + totalCo2Discharge: 53_894_853.608_163_215, + totalCo2Export: 174_956_880, + totalCo2Import: 0, + totalCo2NetExchange: -174_956_880, + totalCo2Production: 1_169_515_098.83, + totalCo2Storage: 0, + totalConsumption: 5597, + totalDischarge: 395, + totalExport: 934, + totalImport: 0, + totalProduction: 6136, + totalStorage: 0, + hasParser: true, + center: [-7.8, 39.6], +}; + +const productionData = [ + { + isStorage: false, + production: null, + storage: undefined, + capacity: 0, + mode: 'nuclear', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: null, + storage: undefined, + capacity: 0, + mode: 'geothermal', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 350, + storage: undefined, + capacity: 700, + mode: 'biomass', + tCo2eqPerMin: 2.561_683_868_333_333, + }, + { + isStorage: false, + production: 0, + storage: undefined, + capacity: 0, + mode: 'coal', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 2365, + storage: undefined, + capacity: 5389, + mode: 'wind', + tCo2eqPerMin: 0.497_438_333_333_333_3, + }, + { + isStorage: false, + production: 17, + storage: undefined, + capacity: 1616, + mode: 'solar', + tCo2eqPerMin: 0.007_253_333_333_333_333, + }, + { + isStorage: false, + storage: -395, + production: 1445, + capacity: 4578, + mode: 'hydro', + tCo2eqPerMin: 0.257_691_666_666_666_65, + }, + { + isStorage: true, + storage: -395, + production: 1445, + capacity: 3585, + mode: 'hydro storage', + tCo2eqPerMin: -0.898_247_560_136_053_7, + }, + { + isStorage: true, + storage: null, + capacity: null, + mode: 'battery storage', + production: undefined, + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 1930, + storage: undefined, + capacity: 4520, + mode: 'gas', + tCo2eqPerMin: 15.829_517_778_833_331, + }, + { + isStorage: false, + production: null, + storage: undefined, + capacity: 0, + mode: 'oil', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 29, + storage: undefined, + capacity: null, + mode: 'unknown', + tCo2eqPerMin: 0.338_333_333_333_333_3, + }, +]; + +const exchangeData = [ + { exchange: -934, mode: 'ES', gCo2eqPerkWh: 187.32, tCo2eqPerMin: -2.915_948 }, + { exchange: 200, mode: 'FR', gCo2eqPerkWh: 999.32, tCo2eqPerMin: 45.915_948 }, +]; + +describe('getProductionData', () => { + it('returns correct data', () => { + const result = getProductionData(zoneDetailsData); + // TODO: Match snapshot + expect(result).toStrictEqual(productionData); + }); +}); + +describe('getDataBlockPositions', () => { + it('returns correct data', () => { + const result = getDataBlockPositions(productionData.length, exchangeData); + expect(result).toStrictEqual({ + exchangeFlagX: 50, + exchangeHeight: 40, + exchangeY: 282, + productionY: 22, + productionHeight: 240, + }); + }); +}); diff --git a/web/src/features/charts/bar-breakdown/utils.ts b/web/src/features/charts/bar-breakdown/utils.ts new file mode 100644 index 0000000000..42659c3032 --- /dev/null +++ b/web/src/features/charts/bar-breakdown/utils.ts @@ -0,0 +1,187 @@ +import { max as d3Max } from 'd3-array'; +import { + ElectricityModeType, + ElectricityStorageKeyType, + Exchange, + GenerationType, + Maybe, + ZoneDetail, + ZoneKey, +} from 'types'; +import { Mode, modeOrder } from 'utils/constants'; +import { getCO2IntensityByMode } from 'utils/helpers'; +import exchangesToExclude from '../../../../config/excludedAggregatedExchanges.json'; // TODO: do something globally + +const LABEL_MAX_WIDTH = 102; +const ROW_HEIGHT = 13; +const PADDING_Y = 7; +const PADDING_X = 5; +const X_AXIS_HEIGHT = 15; +const DEFAULT_FLAG_SIZE = 16; + +export function getProductionCo2Intensity( + mode: ElectricityModeType, + zoneData: ZoneDetail +) { + const isStorage = mode.includes('storage'); + const generationMode = mode.replace(' storage', '') as GenerationType; + + if (!isStorage) { + return zoneData.productionCo2Intensities?.[generationMode]; + } + + const storage = zoneData.storage?.[generationMode as ElectricityStorageKeyType]; + // TODO: Find out how this worked before if the data is never available + const storageCo2Intensity = zoneData.storageCo2Intensities?.[generationMode]; + const dischargeCo2Intensity = + zoneData.dischargeCo2Intensities?.[generationMode as ElectricityStorageKeyType]; + + return storage && storage > 0 ? storageCo2Intensity : dischargeCo2Intensity; +} + +export function getExchangeCo2Intensity(mode, zoneData, electricityMixMode) { + const exchange = (zoneData.exchange || {})[mode]; + const exchangeCo2Intensity = (zoneData.exchangeCo2Intensities || {})[mode]; + + if (exchange >= 0) { + return exchangeCo2Intensity; + } + + return getCO2IntensityByMode(zoneData, electricityMixMode); +} + +export interface ProductionDataType { + production: Maybe; + capacity: Maybe; + isStorage: boolean; + storage: Maybe; + mode: ElectricityModeType; + tCo2eqPerMin: number; +} + +export const getProductionData = (data: ZoneDetail): ProductionDataType[] => + modeOrder.map((mode) => { + const isStorage = mode.includes('storage'); + const generationMode = mode.replace(' storage', '') as GenerationType; + // Power in MW + + const capacity = data.capacity?.[mode]; + const production = data.production?.[generationMode]; + const storage = data.storage?.[generationMode as ElectricityStorageKeyType]; + + // Production CO₂ intensity + const gCo2eqPerkWh = getProductionCo2Intensity(mode, data); + const value = isStorage && storage ? storage : production || 0; + const gCo2eqPerHour = gCo2eqPerkWh * 1e3 * value; + const tCo2eqPerMin = gCo2eqPerHour / 1e6 / 60; + + return { + isStorage, + storage, + production, + capacity, + mode, + tCo2eqPerMin, + }; + }); + +interface GetElectricityProductionValueType { + capacity: number; + isStorage: boolean; + production: number; + storage: number; +} +export function getElectricityProductionValue({ + capacity, + isStorage, + production, + storage, +}: GetElectricityProductionValueType) { + const value = isStorage ? -storage : production; + // If the value is not defined but the capacity + // is zero, assume the value is also zero. + if (!Number.isFinite(value) && capacity === 0) { + return 0; + } + return value; +} + +export const getDataBlockPositions = ( + prouductionLength: number, + exchangeData: ExchangeDataType[] +) => { + const productionHeight = prouductionLength * (ROW_HEIGHT + PADDING_Y); + const productionY = X_AXIS_HEIGHT + PADDING_Y; + + const exchangeMax = d3Max(exchangeData, (d) => d.mode.length) || 0; + + const exchangeFlagX = + LABEL_MAX_WIDTH - 4 * PADDING_X - DEFAULT_FLAG_SIZE - exchangeMax * 8; + const exchangeHeight = exchangeData.length * (ROW_HEIGHT + PADDING_Y); + const exchangeY = productionY + productionHeight + ROW_HEIGHT + PADDING_Y; + + return { + productionHeight, + productionY, + exchangeFlagX, + exchangeHeight, + exchangeY, + }; +}; + +export interface ExchangeDataType { + exchange: number; + mode: ZoneKey; // TODO: Weird that this is called "mode" + gCo2eqPerkWh: number; + tCo2eqPerMin: number; +} +export const getExchangeData = ( + data: ZoneDetail, + exchangeKeys: string[], + electricityMixMode: Mode +): ExchangeDataType[] => + exchangeKeys.map((mode) => { + // Power in MW + const exchange = (data.exchange || {})[mode]; + const exchangeCapacityRange = (data.exchangeCapacities || {})[mode]; + + // Exchange CO₂ intensity + const gCo2eqPerkWh = getExchangeCo2Intensity(mode, data, electricityMixMode); + const gCo2eqPerHour = gCo2eqPerkWh * 1e3 * exchange; + const tCo2eqPerMin = gCo2eqPerHour / 1e6 / 60; + + return { + exchange, + exchangeCapacityRange, + mode, + gCo2eqPerkWh, + tCo2eqPerMin, + }; + }); + +export const getExchangesToDisplay = ( + currentZoneKey: ZoneKey, + isAggregatedToggled: boolean, + exchangeZoneKeysForCurrentZone: Exchange +): ZoneKey[] => { + const exchangeKeysToRemove = isAggregatedToggled + ? exchangesToExclude.exchangesToExcludeCountryView + : exchangesToExclude.exchangesToExcludeZoneView; + + const exchangeZoneKeysToRemove = new Set( + exchangeKeysToRemove.flatMap((exchangeKey) => { + const split = exchangeKey.split('->'); + if (split.includes(currentZoneKey)) { + return split.filter((exchangeKey) => exchangeKey !== currentZoneKey); + } + return []; + }) + ); + + const currentExchanges = Object.keys(exchangeZoneKeysForCurrentZone); + return currentExchanges + ? currentExchanges.filter( + (exchangeZoneKey) => !exchangeZoneKeysToRemove.has(exchangeZoneKey) + ) + : []; +}; diff --git a/web/src/features/charts/hooks/useBarBreakdownProductionChartData.ts b/web/src/features/charts/hooks/useBarBreakdownProductionChartData.ts new file mode 100644 index 0000000000..c1b85c58cf --- /dev/null +++ b/web/src/features/charts/hooks/useBarBreakdownProductionChartData.ts @@ -0,0 +1,64 @@ +import useGetZone from 'api/getZone'; +import { useAtom } from 'jotai'; +import { useParams } from 'react-router-dom'; +import { ToggleOptions } from 'utils/constants'; +import { + productionConsumptionAtom, + selectedDatetimeIndexAtom, + spatialAggregateAtom, +} from 'utils/state/atoms'; +import { + getDataBlockPositions, + getExchangeData, + getExchangesToDisplay, + getProductionData, +} from '../bar-breakdown/utils'; + +export default function useBarBreakdownChartData() { + // TODO: Create hook for using "current" selectedTimeIndex of data instead + const { data: zoneData, isLoading } = useGetZone(); + const { zoneId } = useParams(); + const [aggregateToggle] = useAtom(spatialAggregateAtom); + const [selectedDatetime] = useAtom(selectedDatetimeIndexAtom); + const [mixMode] = useAtom(productionConsumptionAtom); + const isAggregateToggled = aggregateToggle === ToggleOptions.ON; + const currentData = zoneData?.zoneStates?.[selectedDatetime.datetimeString]; + if ( + isLoading || + !zoneId || + !zoneData || + !selectedDatetime.datetimeString || + !currentData + ) { + return { + height: 0, + data: undefined, + exchangeData: [], + productionData: [], + isLoading: true, + }; + } + + const exchangeKeys = getExchangesToDisplay( + zoneId, + isAggregateToggled, + currentData.exchange + ); + + const productionData = getProductionData(currentData); // TODO: Consider memoing this + const exchangeData = getExchangeData(currentData, exchangeKeys, mixMode); // TODO: Consider memoing this + + const { exchangeY, exchangeHeight } = getDataBlockPositions( + productionData.length, + exchangeData + ); + const height = exchangeY + exchangeHeight; + + return { + height, + data: currentData, // TODO: Data is returned here just to pass it back to the tooltip + exchangeData, + productionData, + isLoading: false, + }; +} diff --git a/web/src/features/panels/LeftPanel.tsx b/web/src/features/panels/LeftPanel.tsx index a347510e40..2ec3dfa1a1 100644 --- a/web/src/features/panels/LeftPanel.tsx +++ b/web/src/features/panels/LeftPanel.tsx @@ -44,7 +44,7 @@ function OuterPanel({ children }: { children: React.ReactNode }) { !isOpen && '-translate-x-full' }`} > -
{children}
+
{children}
); diff --git a/web/src/features/panels/ranking-panel/RankingPanel.tsx b/web/src/features/panels/ranking-panel/RankingPanel.tsx index f54da0f826..1fd702cf3b 100644 --- a/web/src/features/panels/ranking-panel/RankingPanel.tsx +++ b/web/src/features/panels/ranking-panel/RankingPanel.tsx @@ -2,7 +2,11 @@ import useGetState from 'api/getState'; import { useCo2ColorScale } from 'hooks/theme'; import { useAtom } from 'jotai'; import { ReactElement, useState } from 'react'; -import { selectedDatetimeIndexAtom, productionConsumptionAtom, timeAverageAtom } from 'utils/state/atoms'; +import { + productionConsumptionAtom, + selectedDatetimeIndexAtom, + timeAverageAtom, +} from 'utils/state/atoms'; import { useTranslation } from '../../../translation/translation'; import { getRankedState } from './getRankingPanelData'; import SearchBar from './SearchBar'; diff --git a/web/src/features/panels/ranking-panel/getRankingPanelData.ts b/web/src/features/panels/ranking-panel/getRankingPanelData.ts index 9d77e70828..00c6789b88 100644 --- a/web/src/features/panels/ranking-panel/getRankingPanelData.ts +++ b/web/src/features/panels/ranking-panel/getRankingPanelData.ts @@ -1,8 +1,6 @@ -import { useAtom } from 'jotai'; import { getCountryName, getZoneName } from 'translation/translation'; import type { GridState } from 'types'; import { getCO2IntensityByMode } from 'utils/helpers'; -import { productionConsumptionAtom } from 'utils/state'; import { ZoneRowType } from './ZoneList'; export const getRankedState = ( diff --git a/web/src/features/panels/zone/ZoneDetails.tsx b/web/src/features/panels/zone/ZoneDetails.tsx index 2a676e0eae..d6a7bf59c7 100644 --- a/web/src/features/panels/zone/ZoneDetails.tsx +++ b/web/src/features/panels/zone/ZoneDetails.tsx @@ -1,4 +1,5 @@ import useGetZone from 'api/getZone'; +import BarBreakdownChart from 'features/charts/bar-breakdown/BarBreakdownChart'; import BreakdownChart from 'features/charts/BreakdownChart'; import CarbonChart from 'features/charts/CarbonChart'; import EmissionChart from 'features/charts/EmissionChart'; @@ -62,6 +63,7 @@ export default function ZoneDetails(): JSX.Element { renewableRatio={renewableRatio} /> + {displayByEmissions ? ( ) : ( diff --git a/web/src/stories/charts/BarBreakdownEmissionsChart.stories.ts b/web/src/stories/charts/BarBreakdownEmissionsChart.stories.ts new file mode 100644 index 0000000000..80016497af --- /dev/null +++ b/web/src/stories/charts/BarBreakdownEmissionsChart.stories.ts @@ -0,0 +1,202 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import BarBreakdownEmissionsChart from 'features/charts/bar-breakdown/BarBreakdownEmissionsChart'; + +const meta: Meta = { + title: 'charts/BarBreakdownEmissionsChart', + component: BarBreakdownEmissionsChart, +}; + +type Story = StoryObj; + +const data = { + co2intensity: 187.32, + co2intensityProduction: 190.6, + countryCode: 'PT', + fossilFuelRatio: 0.3, + fossilFuelRatioProduction: 0.3, + renewableRatio: 0.7, + renewableRatioProduction: 0.7, + stateDatetime: '2022-11-28T07:00:00.000Z', + _isFinestGranularity: true, + capacity: { + 'battery storage': null, + biomass: 700, + coal: 0, + gas: 4520, + geothermal: 0, + hydro: 4578, + 'hydro storage': 3585, + nuclear: 0, + oil: 0, + solar: 1616, + unknown: null, + wind: 5389, + }, + dischargeCo2Intensities: { + battery: 136.442_667_362_438_53, + hydro: 136.442_667_362_438_53, + }, + dischargeCo2IntensitySources: { + battery: 'electricityMap, 2021 average', + hydro: 'electricityMap, 2021 average', + }, + exchange: { ES: -934 }, + exchangeCapacities: {}, + exchangeCo2Intensities: { ES: 187.32 }, + isValid: true, + maxCapacity: 5389, + maxDischarge: 395, + maxExport: 934, + maxImport: 0, + maxProduction: 2365, + maxStorage: 0, + maxStorageCapacity: 3585, + price: { currency: 'EUR', value: 120 }, + production: { + biomass: 350, + coal: 0, + gas: 1930, + geothermal: null, + hydro: 1445, + nuclear: null, + oil: null, + solar: 17, + unknown: 29, + wind: 2365, + }, + productionCo2Intensities: { + biomass: 439.145_806, + coal: 1099.976_527, + gas: 492.109_361, + geothermal: 38, + hydro: 10.7, + nuclear: 5.13, + oil: 1124.903_938, + solar: 25.6, + unknown: 700, + wind: 12.62, + }, + productionCo2IntensitySources: { + biomass: 'EU-ETS, ENTSO-E 2021', + coal: 'eGrid 2020; Oberschelp, Christopher, et al. "Global emission hotspots of coal power generation."', + gas: 'IPCC 2014; EU-ETS, ENTSO-E 2021', + geothermal: 'IPCC 2014', + hydro: 'UNECE 2022', + nuclear: 'UNECE 2022', + oil: 'IPCC 2014; EU-ETS, ENTSO-E 2021', + solar: 'INCER ACV', + unknown: 'assumes thermal (coal, gas, oil or biomass)', + wind: 'UNECE 2022, WindEurope "Wind energy in Europe, 2021 Statistics and the outlook for 2022-2026" Wind Europe Proceedings (2021)', + }, + source: ['entsoe.eu'], + storage: { battery: null, hydro: -395 }, + totalCo2Discharge: 53_894_853.608_163_215, + totalCo2Export: 174_956_880, + totalCo2Import: 0, + totalCo2NetExchange: -174_956_880, + totalCo2Production: 1_169_515_098.83, + totalCo2Storage: 0, + totalConsumption: 5597, + totalDischarge: 395, + totalExport: 934, + totalImport: 0, + totalProduction: 6136, + totalStorage: 0, + hasParser: true, + center: [-7.8, 39.6], +}; + +const productionData = [ + { isStorage: false, production: null, capacity: 0, mode: 'nuclear', tCo2eqPerMin: 0 }, + { + isStorage: false, + production: null, + capacity: 0, + mode: 'geothermal', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 350, + capacity: 700, + mode: 'biomass', + tCo2eqPerMin: 2.561_683_868_333_333, + }, + { isStorage: false, production: 0, capacity: 0, mode: 'coal', tCo2eqPerMin: 0 }, + { + isStorage: false, + production: 2365, + capacity: 5389, + mode: 'wind', + tCo2eqPerMin: 0.497_438_333_333_333_3, + }, + { + isStorage: false, + production: 17, + capacity: 1616, + mode: 'solar', + tCo2eqPerMin: 0.007_253_333_333_333_333, + }, + { + isStorage: false, + storage: -395, + production: 1445, + capacity: 4578, + mode: 'hydro', + tCo2eqPerMin: 0.257_691_666_666_666_65, + }, + { + isStorage: true, + storage: -395, + production: 1445, + capacity: 3585, + mode: 'hydro storage', + tCo2eqPerMin: -0.898_247_560_136_053_7, + }, + { + isStorage: true, + storage: null, + capacity: null, + mode: 'battery storage', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 1930, + capacity: 4520, + mode: 'gas', + tCo2eqPerMin: 15.829_517_778_833_331, + }, + { isStorage: false, production: null, capacity: 0, mode: 'oil', tCo2eqPerMin: 0 }, + { + isStorage: false, + production: 29, + capacity: null, + mode: 'unknown', + tCo2eqPerMin: 0.338_333_333_333_333_3, + }, +]; + +const exchangeData = [ + { exchange: -934, mode: 'ES', gCo2eqPerkWh: 187.32, tCo2eqPerMin: -2.915_948 }, + { exchange: 200, mode: 'FR', gCo2eqPerkWh: 999.32, tCo2eqPerMin: 1.915_948 }, +]; + +export const IncludesStorage: Story = { + // More on args: https://storybook.js.org/docs/react/writing-stories/args + args: { + //testId: 'none', + data: data, + productionData: productionData, + exchangeData: exchangeData, + onExchangeRowMouseOut: () => {}, + onExchangeRowMouseOver: () => {}, + onProductionRowMouseOut: () => {}, + onProductionRowMouseOver: () => {}, + width: 300, + height: 300, + isMobile: false, + }, +}; + +export default meta; diff --git a/web/src/stories/charts/BarBreakdownProductionChart.stories.ts b/web/src/stories/charts/BarBreakdownProductionChart.stories.ts new file mode 100644 index 0000000000..9dcf183b4a --- /dev/null +++ b/web/src/stories/charts/BarBreakdownProductionChart.stories.ts @@ -0,0 +1,202 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import BarBreakdownProductionChart from 'features/charts/bar-breakdown/BarBreakdownProductionChart'; + +const meta: Meta = { + title: 'charts/BarBreakdownProductionChart', + component: BarBreakdownProductionChart, +}; + +type Story = StoryObj; + +const data = { + co2intensity: 187.32, + co2intensityProduction: 190.6, + countryCode: 'PT', + fossilFuelRatio: 0.3, + fossilFuelRatioProduction: 0.3, + renewableRatio: 0.7, + renewableRatioProduction: 0.7, + stateDatetime: '2022-11-28T07:00:00.000Z', + _isFinestGranularity: true, + capacity: { + 'battery storage': null, + biomass: 700, + coal: 0, + gas: 4520, + geothermal: 0, + hydro: 4578, + 'hydro storage': 3585, + nuclear: 0, + oil: 0, + solar: 1616, + unknown: null, + wind: 5389, + }, + dischargeCo2Intensities: { + battery: 136.442_667_362_438_53, + hydro: 136.442_667_362_438_53, + }, + dischargeCo2IntensitySources: { + battery: 'electricityMap, 2021 average', + hydro: 'electricityMap, 2021 average', + }, + exchange: { ES: -934 }, + exchangeCapacities: {}, + exchangeCo2Intensities: { ES: 187.32 }, + isValid: true, + maxCapacity: 5389, + maxDischarge: 395, + maxExport: 934, + maxImport: 0, + maxProduction: 2365, + maxStorage: 0, + maxStorageCapacity: 3585, + price: { currency: 'EUR', value: 120 }, + production: { + biomass: 350, + coal: 0, + gas: 1930, + geothermal: null, + hydro: 1445, + nuclear: null, + oil: null, + solar: 17, + unknown: 29, + wind: 2365, + }, + productionCo2Intensities: { + biomass: 439.145_806, + coal: 1099.976_527, + gas: 492.109_361, + geothermal: 38, + hydro: 10.7, + nuclear: 5.13, + oil: 1124.903_938, + solar: 25.6, + unknown: 700, + wind: 12.62, + }, + productionCo2IntensitySources: { + biomass: 'EU-ETS, ENTSO-E 2021', + coal: 'eGrid 2020; Oberschelp, Christopher, et al. "Global emission hotspots of coal power generation."', + gas: 'IPCC 2014; EU-ETS, ENTSO-E 2021', + geothermal: 'IPCC 2014', + hydro: 'UNECE 2022', + nuclear: 'UNECE 2022', + oil: 'IPCC 2014; EU-ETS, ENTSO-E 2021', + solar: 'INCER ACV', + unknown: 'assumes thermal (coal, gas, oil or biomass)', + wind: 'UNECE 2022, WindEurope "Wind energy in Europe, 2021 Statistics and the outlook for 2022-2026" Wind Europe Proceedings (2021)', + }, + source: ['entsoe.eu'], + storage: { battery: null, hydro: -395 }, + totalCo2Discharge: 53_894_853.608_163_215, + totalCo2Export: 174_956_880, + totalCo2Import: 0, + totalCo2NetExchange: -174_956_880, + totalCo2Production: 1_169_515_098.83, + totalCo2Storage: 0, + totalConsumption: 5597, + totalDischarge: 395, + totalExport: 934, + totalImport: 0, + totalProduction: 6136, + totalStorage: 0, + hasParser: true, + center: [-7.8, 39.6], +}; + +const productionData = [ + { isStorage: false, production: null, capacity: 0, mode: 'nuclear', tCo2eqPerMin: 0 }, + { + isStorage: false, + production: null, + capacity: 0, + mode: 'geothermal', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 350, + capacity: 700, + mode: 'biomass', + tCo2eqPerMin: 2.561_683_868_333_333, + }, + { isStorage: false, production: 0, capacity: 0, mode: 'coal', tCo2eqPerMin: 0 }, + { + isStorage: false, + production: 2365, + capacity: 5389, + mode: 'wind', + tCo2eqPerMin: 0.497_438_333_333_333_3, + }, + { + isStorage: false, + production: 17, + capacity: 1616, + mode: 'solar', + tCo2eqPerMin: 0.007_253_333_333_333_333, + }, + { + isStorage: false, + storage: -395, + production: 1445, + capacity: 4578, + mode: 'hydro', + tCo2eqPerMin: 0.257_691_666_666_666_65, + }, + { + isStorage: true, + storage: -395, + production: 1445, + capacity: 3585, + mode: 'hydro storage', + tCo2eqPerMin: -0.898_247_560_136_053_7, + }, + { + isStorage: true, + storage: null, + capacity: null, + mode: 'battery storage', + tCo2eqPerMin: 0, + }, + { + isStorage: false, + production: 1930, + capacity: 4520, + mode: 'gas', + tCo2eqPerMin: 15.829_517_778_833_331, + }, + { isStorage: false, production: null, capacity: 0, mode: 'oil', tCo2eqPerMin: 0 }, + { + isStorage: false, + production: 29, + capacity: null, + mode: 'unknown', + tCo2eqPerMin: 0.338_333_333_333_333_3, + }, +]; + +const exchangeData = [ + { exchange: -934, mode: 'ES', gCo2eqPerkWh: 187.32, tCo2eqPerMin: -2.915_948 }, + { exchange: 200, mode: 'FR', gCo2eqPerkWh: 999.32, tCo2eqPerMin: 45.915_948 }, +]; + +export const IncludesStorage: Story = { + // More on args: https://storybook.js.org/docs/react/writing-stories/args + args: { + //testId: 'none', + data: data, + productionData: productionData, + exchangeData: exchangeData, + onExchangeRowMouseOut: () => {}, + onExchangeRowMouseOver: () => {}, + onProductionRowMouseOut: () => {}, + onProductionRowMouseOver: () => {}, + width: 300, + height: 300, + isMobile: false, + }, +}; + +export default meta; diff --git a/web/src/types.ts b/web/src/types.ts index 9507c000bb..4c3bab6011 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -1,5 +1,9 @@ import { Feature, FeatureCollection, Geometry, MultiPolygon, Polygon } from '@turf/turf'; +export type Maybe = T | null | undefined; + +export type ZoneKey = string; + export interface GridState { callerLocation?: [number, number]; data: { @@ -47,13 +51,9 @@ export interface ZoneOverviewForTimePeriod { [dateTimeKey: string]: ZoneOverview; } export interface ZoneOverview { - countryCode: string; + zoneKey: string; co2intensity?: number; co2intensityProduction?: number; - consumptionColour?: string; - productionColour?: string; - colorBlindConsumptionColour?: string; - colorBlindProductionColour?: string; stateDatetime: string; fossilFuelRatio: number; renewableRatio: number; @@ -70,41 +70,70 @@ export type GenerationType = | 'solar' | 'unknown' | 'geothermal' - | 'hydro storage' // storage should perhaps be separated - | 'battery storage' // storage should perhaps be separated | 'wind'; +export type ElectricityStorageType = 'hydro storage' | 'battery storage'; +export type ElectricityStorageKeyType = 'battery' | 'hydro'; + +export type ElectricityModeType = GenerationType | ElectricityStorageType; + +export type Exchange = { [key: string]: number }; + export interface ZoneDetail extends ZoneOverview { - production: { [key in GenerationType]: number }; // TODO: this assumes all modes are present - capacity: { [key in GenerationType]: number }; - exchange: { [key: string]: number }; - co2intensity: number; - co2intensityProduction: number; - totalco2intensity: number; - totalCo2Import: number; - totalCo2Discharge: number; - totalCo2Production: number; - totalProduction: number; - totalImport: number; - totalDischarge: number; - dischargeCo2Intensities: { [key in ElectricityStorageType]: number }; - productionCo2Intensities: { [key in GenerationType]: number }; - exchangeCo2Intensities: { [key: string]: number }; - storage: { [key in ElectricityStorageType]: number }; + _isFinestGranularity: boolean; + capacity: { [key in ElectricityModeType]: Maybe }; + dischargeCo2Intensities: { [key in ElectricityStorageKeyType]: number }; + dischargeCo2IntensitySources: { [key in ElectricityStorageKeyType]: string }; + exchange: Exchange; + exchangeCapacities?: { + [key: string]: number[]; // TODO: Why can I not use [number, number] here? + }; + exchangeCo2Intensities: Exchange; + fossilFuelRatio: number; + fossilFuelRatioProduction: number; + isValid: boolean; + maxCapacity: number; + maxDischarge: number; + maxExport: number; + maxExportCapacity: number; + maxImport: number; + maxImportCapacity: number; + maxProduction: number; + maxStorage: number; + maxStorageCapacity: number; price?: { value: number; currency: string; }; + production: { [key in GenerationType]: Maybe }; + productionCo2Intensities: { [key in GenerationType]: number }; + productionCo2IntensitySources: { [key in GenerationType]: string }; + renewableRatio: number; + renewableRatioProduction: number; + source: string; + storage: { [key in ElectricityStorageKeyType]: Maybe }; + totalCo2Discharge: number; + totalCo2Export: number; + totalCo2Import: number; + totalCo2NetExchange: number; + totalCo2Production: number; + totalCo2Storage: number; + totalConsumption: number; + totalDischarge: number; + totalExport: number; + totalImport: number; + totalProduction: number; + totalStorage: number; } export interface ZoneDetails { hasData: boolean; stateAggregation: 'daily' | 'hourly' | 'monthly' | 'yearly'; - zoneStates: { [key: string]: ZoneDetail }; + zoneStates: { + [key: string]: ZoneDetail; + }; } -export type ElectricityStorageType = 'battery storage' | 'hydro storage'; - export interface MapGeometries extends FeatureCollection { features: Array; } diff --git a/web/src/utils/constants.ts b/web/src/utils/constants.ts index 8d3fe46ea0..9c4ebcf4e0 100644 --- a/web/src/utils/constants.ts +++ b/web/src/utils/constants.ts @@ -55,4 +55,4 @@ export const modeOrder = [ 'gas', 'oil', 'unknown', -]; +] as const;