From 5a199d14fd9245e20cd7db0da1310b6fe858907b Mon Sep 17 00:00:00 2001 From: Kav91 Date: Fri, 1 Sep 2023 21:30:45 +1000 Subject: [PATCH 01/27] feat: add bar line chart --- package-lock.json | 282 ++++++++++++- package.json | 4 +- .../nr-line-bar-chart/docs/index.js | 51 +++ visualizations/nr-line-bar-chart/index.js | 389 ++++++++++++++++++ visualizations/nr-line-bar-chart/nr1.json | 140 +++++++ visualizations/nr-line-bar-chart/styles.scss | 0 6 files changed, 863 insertions(+), 3 deletions(-) create mode 100644 visualizations/nr-line-bar-chart/docs/index.js create mode 100644 visualizations/nr-line-bar-chart/index.js create mode 100644 visualizations/nr-line-bar-chart/nr1.json create mode 100644 visualizations/nr-line-bar-chart/styles.scss diff --git a/package-lock.json b/package-lock.json index 245dfbe..e67bf5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,6 +87,14 @@ "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", "dev": true }, + "@babel/runtime": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", + "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, "@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -307,6 +315,60 @@ "defer-to-connect": "^1.0.1" } }, + "@types/d3-array": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.7.tgz", + "integrity": "sha512-4/Q0FckQ8TBjsB0VdGFemJOG8BLXUB2KKlL0VmZ+eOYeOnTb/wDRQqYWpBmQ6IlvWkXwkYiot+n9Px2aTJ7zGQ==" + }, + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "@types/d3-scale": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.4.tgz", + "integrity": "sha512-eq1ZeTj0yr72L8MQk6N6heP603ubnywSDRfNpi5enouR112HzGLS6RIvExCzZTraFF4HdzNpJMwA/zGiMoHUUw==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-shape": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.2.tgz", + "integrity": "sha512-NN4CXr3qeOUNyK5WasVUV8NCSAx/CRVcwcb0BuuS1PiTqwIm6ABi1SyasLZ/vsVCFDArF+W4QiGzSry1eKYQ7w==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, "@types/eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", @@ -619,6 +681,11 @@ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==" }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -688,11 +755,93 @@ "type-fest": "^0.8.1" } }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "csscolorparser": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" }, + "d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, "date-fns": { "version": "2.29.3", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", @@ -707,6 +856,11 @@ "ms": "2.1.2" } }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -754,6 +908,14 @@ "esutils": "^2.0.2" } }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -1274,6 +1436,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1322,6 +1489,11 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, + "fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1740,6 +1912,11 @@ "side-channel": "^1.0.4" } }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -1976,8 +2153,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.get": { "version": "4.4.2", @@ -2109,6 +2285,11 @@ "minimist": "^1.2.6" } }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2336,6 +2517,11 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, "potpack": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", @@ -2439,6 +2625,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-map-gl": { "version": "7.0.19", "resolved": "https://registry.npmjs.org/react-map-gl/-/react-map-gl-7.0.19.tgz", @@ -2447,6 +2638,72 @@ "@types/mapbox-gl": "^2.6.0" } }, + "react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "requires": { + "lodash": "^4.17.21" + } + }, + "react-smooth": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.3.tgz", + "integrity": "sha512-yl4y3XiMorss7ayF5QnBiSprig0+qFHui8uh7Hgg46QX5O+aRMRKlfGGNGLHno35JkQSvSYY8eCWkBfHfrSHfg==", + "requires": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, + "recharts": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.8.0.tgz", + "integrity": "sha512-nciXqQDh3aW8abhwUlA4EBOBusRHLNiKHfpRZiG/yjups1x+auHb2zWPuEcTn/IMiN47vVMMuF8Sr+vcQJtsmw==", + "requires": { + "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" + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -2901,6 +3158,27 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "victory-vendor": { + "version": "36.6.11", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.11.tgz", + "integrity": "sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg==", + "requires": { + "@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" + } + }, "vt-pbf": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", diff --git a/package.json b/package.json index 265bf25..2ce9209 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,11 @@ "@newrelic/nr1-community": "latest", "chart.js": "^3.9.1", "mapbox-gl": "^2.10.0", + "moment": "^2.29.4", "prop-types": "^15.7.2", "react-chartjs-2": "^4.3.1", - "react-map-gl": "^7.0.19" + "react-map-gl": "^7.0.19", + "recharts": "^2.8.0" }, "ids": { "am": "c2fab1ee-57c3-43d6-bc5c-c7343ecaff0c", diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js new file mode 100644 index 0000000..ede72ea --- /dev/null +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -0,0 +1,51 @@ +import React from 'react'; + +import { + Card, + CardHeader, + CardBody, + HeadingText, + Link + // NrqlQuery, + // Spinner, + // AutoSizer, + // BlockText, + // List, + // ListItem +} from 'nr1'; + +export default function Docs() { + return ( +
+ Documentation + + Example Line Queries + + SELECT count(*) as 'Views' from PageView where TIMESERIES 1 day since + last week + + + + + Example Bar Queries + + SELECT percentile(duration, 80) as 'Load' from PageView TIMESERIES 1 + day since last week + + + + + Tick Format + + The tick format is powered by moment. View their documentation for + other configuration options. + + Moment Date Parsing Documentation. + + + + +
+
+ ); +} diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js new file mode 100644 index 0000000..3956850 --- /dev/null +++ b/visualizations/nr-line-bar-chart/index.js @@ -0,0 +1,389 @@ +import React, { useContext, useState, useEffect } from 'react'; +import { + ComposedChart, + Line, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend +} from 'recharts'; + +import { + Card, + CardBody, + HeadingText, + NrqlQuery, + Spinner, + AutoSizer, + NerdletStateContext, + PlatformStateContext +} from 'nr1'; + +import { useInterval } from '@mantine/hooks'; +import moment from 'moment'; + +import Docs from './docs'; + +const MINUTE = 60000; +const HOUR = 60 * MINUTE; +const DAY = 24 * HOUR; + +const timeRangeToNrql = timeRange => { + if (!timeRange) { + return 'SINCE 30 minutes ago'; + } + + if (timeRange.beginTime && timeRange.endTime) { + return `SINCE ${timeRange.beginTime} UNTIL ${timeRange.endTime}`; + } else if (timeRange.begin_time && timeRange.end_time) { + return `SINCE ${timeRange.begin_time} UNTIL ${timeRange.end_time}`; + } else if (timeRange.duration <= HOUR) { + return `SINCE ${timeRange.duration / MINUTE} MINUTES AGO`; + } else if (timeRange.duration <= DAY) { + return `SINCE ${timeRange.duration / HOUR} HOURS AGO`; + } else { + return `SINCE ${timeRange.duration / DAY} DAYS AGO`; + } +}; + +// const transform = (views, load) => { +// const viewsSeries = views[0].data; +// const loadSeries = load[0].data; + +// console.log(loadSeries); + +// const data = viewsSeries.map((v, i) => { +// const entry = {}; +// const d = new Date(v.begin_time); +// entry.day = d.toDateString(); +// entry.views = v.count; +// entry.load = loadSeries[i].Load; +// return entry; +// }); +// return data; +// }; + +// const buildRechartData = dataSets => { +// const rechartData = { bars: {}, lines: {} }; + +// return rechartData; +// }; + +export default function LineBarChart(props) { + const { + pollInterval, + enableTimePicker, + lineQueries, + barQueries, + showDocs, + tickFormat + } = props; + const [errors, setErrors] = useState([]); + const [dataSets, setDataSets] = useState([]); + const [barData, setBarData] = useState({}); + const [lineData, setLineData] = useState({}); + + const [loading, setLoading] = useState(true); + const platformContext = useContext(PlatformStateContext); + const { filters } = useContext(NerdletStateContext); + const { timeRange } = platformContext; + + useEffect(async () => { + setLoading(true); + const tempErrors = []; + + if ((lineQueries || []).length === 0) { + tempErrors.push({ name: 'You need to supply at least one line query' }); + } else { + lineQueries.forEach((t, i) => { + const { query, accountId } = t; + const lowerQuery = (query || '').toLowerCase(); + const errorObj = { name: `Line Query ${i + 1}`, errors: [] }; + + if (!lowerQuery) { + errorObj.errors.push(`Query is undefined`); + } else if (lowerQuery.trim() === 'from') { + errorObj.errors.push(`Query is undefined`); + } + if (!lowerQuery.includes('timeseries')) { + errorObj.errors.push(`Should contain TIMESERIES keyword`); + } + if (!accountId) { + errorObj.errors.push(`AccountID is undefined`); + } + + if (errorObj.errors.length > 0) { + tempErrors.push(errorObj); + } + }); + } + + if ((barQueries || []).length === 0) { + tempErrors.push({ name: 'You need to supply at least one bar query' }); + } else { + (barQueries || []).forEach((t, i) => { + const { query, accountId, name } = t; + const lowerQuery = (query || '').toLowerCase(); + const errorObj = { name: `Bar Query ${i + 1}`, errors: [] }; + + if (!name) { + errorObj.errors.push(`Name is undefined`); + } + if (!lowerQuery) { + errorObj.errors.push(`Query is undefined`); + } else if (lowerQuery.trim() === 'from') { + errorObj.errors.push(`Query is undefined`); + } + if (!lowerQuery.includes('timeseries')) { + errorObj.errors.push(`Should contain TIMESERIES keyword`); + } + if (!accountId) { + errorObj.errors.push(`AccountID is undefined`); + } + + if (errorObj.errors.length > 0) { + tempErrors.push(errorObj); + } + }); + } + + setErrors(tempErrors); + + setLoading(false); + }, [lineQueries, barQueries]); + + useEffect(() => { + fetchData(); + interval.stop(); + interval.start(); + return interval.stop; + }, [pollInterval, barQueries, lineQueries]); + + const interval = useInterval(() => { + if (lineQueries.length > 0 && errors.length === 0) { + fetchData(); + } + }, (pollInterval || 60) * 1000); + + const doNrql = data => { + return new Promise(resolve => { + const { query, accountId, name, color, type } = data; + NrqlQuery.query({ query, accountIds: [accountId] }).then(value => { + resolve({ ...value, name, color, type }); + }); + }); + }; + + const fetchData = async () => { + const queries = []; + const timeQuery = timeRangeToNrql(timeRange); + + (lineQueries || []).forEach(q => { + const { accountId, query, enableFilters, color, name } = q; + + /* eslint-disable */ + const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${enableTimePicker ? timeQuery : '' + }`; + /* eslint-enable */ + + queries.push({ query: newQuery, accountId, type: 'line', color, name }); + }); + + (barQueries || []) + .filter(e => e.accountId && e.query && e.name) + .forEach(q => { + const { accountId, query, enableFilters, color, name, barSize } = q; + + /* eslint-disable */ + const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${enableTimePicker ? timeQuery : '' + }`; + /* eslint-enable */ + + queries.push({ + query: newQuery, + accountId, + type: 'bar', + color, + name, + barSize + }); + }); + + // eslint-disable-next-line + console.log(`fetching data ${new Date().toLocaleTimeString()}`); + // eslint-disable-next-line + console.log(`queries ${JSON.stringify(queries)}`); + + const nrqlData = await Promise.all(queries.map(q => doNrql(q))); + + // perform data merging + const finalData = []; + const lineData = {}; + const barData = {}; + + nrqlData.forEach(d => { + const { data, color, name, type, barSize } = d; + + data.forEach(groupData => { + const hasFacet = groupData.metadata.groups.find( + g => g.type === 'facet' + ); + groupData.metadata.color = hasFacet ? groupData.metadata.color : color; + + const dataKey = groupData.metadata.groups.find( + g => g.type === 'function' + )?.value; + + groupData.data.forEach(gd => { + if (dataKey) { + const value = gd[dataKey]; + + const baseName = `${name} - ${groupData.metadata.name}`; + + /* eslint-disable */ + const entry = { + time: gd.begin_time, + [`${type === 'line' ? 'L' : 'B'}:${baseName + }`]: value + }; + /* eslint-enable */ + + finalData.push(entry); + + if (type === 'line') { + if (!lineData[`L:${baseName}`]) { + lineData[`L:${baseName}`] = { + results: [entry], + name, + color: groupData.metadata.color + }; + } else { + lineData[`L:${baseName}`].results.push(entry); + } + } else if (type === 'bar') { + if (!barData[`B:${baseName}`]) { + barData[`B:${baseName}`] = { + results: [entry], + barSize, + name, + color: groupData.metadata.color + }; + } else { + barData[`B:${baseName}`].results.push(entry); + } + } + } + }); + }); + }); + + setBarData(barData); + setLineData(lineData); + setDataSets(finalData); + }; + + if (loading) { + return ; + } + + if (errors.length > 0) { + return ErrorState(errors, showDocs); + } + + return ( + + {({ width, height }) => ( + <> + {showDocs && } + + + + moment(unixTime).format(tickFormat || 'YYYY-MM-DD') + } + type="number" + /> + {/* */} + + + + + + {Object.keys(barData).map(bar => ( + + ))} + + {Object.keys(lineData).map(line => ( + + ))} + + + )} + + ); +} + +const ErrorState = (errors, showDocs) => ( + + + {showDocs && } + + + Oops! Something went wrong. + + + + Enable the documentation toggle in the edit settings for more help. + + +
+ + {JSON.stringify(errors)} + + {/* {(errors || []).map(err => ( + <> + {err.name} + + + {(err?.errors || []).map((err, i) => ( + {err} + ))} + + +
+ + ))} */} +
+
+); diff --git a/visualizations/nr-line-bar-chart/nr1.json b/visualizations/nr-line-bar-chart/nr1.json new file mode 100644 index 0000000..7890fdf --- /dev/null +++ b/visualizations/nr-line-bar-chart/nr1.json @@ -0,0 +1,140 @@ +{ + "schemaType": "VISUALIZATION", + "id": "nr-line-bar-chart", + "displayName": "Line & Bar Chart", + "description": "Labs Widget Pack -Line & Bar Chart w/Custom Poll Intervals", + "configuration": [ + { + "name": "showDocs", + "title": "Show Documentation", + "description": "", + "type": "boolean" + }, + { + "name": "tickFormat", + "title": "Moment Tick Formatter (default: YYYY-MM-DD)", + "description": "", + "type": "string" + }, + { + "name": "pollInterval", + "title": "Select Poll Interval (seconds) default 60s", + "description": "", + "type": "enum", + "items": [ + { + "title": "Select", + "value": 60 + }, + { + "title": "5s", + "value": 5 + }, + { + "title": "10s", + "value": 10 + }, + { + "title": "15s", + "value": 15 + }, + { + "title": "30s", + "value": 30 + }, + { + "title": "45s", + "value": 45 + }, + { + "title": "1m", + "value": 60 + }, + { + "title": "5m", + "value": 300 + } + ] + }, + { + "name": "barQueries", + "title": "Bar Queries", + "type": "collection", + "items": [ + { + "name": "name", + "title": "Name of Bar Query", + "description": "", + "type": "string" + }, + { + "name": "accountId", + "title": "Account ID", + "description": "Account ID to be associated with the query", + "type": "account-id" + }, + { + "name": "query", + "title": "Query", + "description": "NRQL query for the visualization", + "type": "nrql" + }, + { + "name": "enableFilters", + "title": "Enable dashboard filters", + "description": "Allows the use of dashboard filters", + "type": "boolean" + }, + { + "name": "barSize", + "title": "Bar Size (default: 20)", + "description": "", + "type": "number" + }, + { + "name": "color", + "title": "Fill color (default: randomized) - Disabled with FACET query", + "description": "", + "type": "string" + } + ] + }, + { + "name": "lineQueries", + "title": "Line Queries", + "type": "collection", + "items": [ + { + "name": "name", + "title": "Name of Line Query", + "description": "", + "type": "string" + }, + { + "name": "accountId", + "title": "Account ID", + "description": "Account ID to be associated with the query", + "type": "account-id" + }, + { + "name": "query", + "title": "Query", + "description": "NRQL query for the visualization", + "type": "nrql" + }, + { + "name": "enableFilters", + "title": "Enable dashboard filters", + "description": "Allows the use of dashboard filters", + "type": "boolean" + }, + { + "name": "color", + "title": "Fill color (default: randomized) - Disabled with FACET query", + "description": "", + "type": "string" + } + ] + } + ] +} \ No newline at end of file diff --git a/visualizations/nr-line-bar-chart/styles.scss b/visualizations/nr-line-bar-chart/styles.scss new file mode 100644 index 0000000..e69de29 From 2a7e75f5dc2425a3038a74a1f7a04449cc58260d Mon Sep 17 00:00:00 2001 From: Kav91 Date: Fri, 1 Sep 2023 21:43:00 +1000 Subject: [PATCH 02/27] feat: re-enable errors --- visualizations/nr-line-bar-chart/index.js | 32 ++++------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index 3956850..f6ad432 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -17,6 +17,9 @@ import { NrqlQuery, Spinner, AutoSizer, + BlockText, + List, + ListItem, NerdletStateContext, PlatformStateContext } from 'nr1'; @@ -48,29 +51,6 @@ const timeRangeToNrql = timeRange => { } }; -// const transform = (views, load) => { -// const viewsSeries = views[0].data; -// const loadSeries = load[0].data; - -// console.log(loadSeries); - -// const data = viewsSeries.map((v, i) => { -// const entry = {}; -// const d = new Date(v.begin_time); -// entry.day = d.toDateString(); -// entry.views = v.count; -// entry.load = loadSeries[i].Load; -// return entry; -// }); -// return data; -// }; - -// const buildRechartData = dataSets => { -// const rechartData = { bars: {}, lines: {} }; - -// return rechartData; -// }; - export default function LineBarChart(props) { const { pollInterval, @@ -369,9 +349,7 @@ const ErrorState = (errors, showDocs) => (
- {JSON.stringify(errors)} - - {/* {(errors || []).map(err => ( + {(errors || []).map(err => ( <> {err.name} @@ -383,7 +361,7 @@ const ErrorState = (errors, showDocs) => (
- ))} */} + ))} ); From 44c8a4fa8de17394d1c3f60dd632043d1a4dc6c6 Mon Sep 17 00:00:00 2001 From: Kav91 Date: Fri, 1 Sep 2023 21:44:14 +1000 Subject: [PATCH 03/27] feat: add bar line chart --- visualizations/nr-line-bar-chart/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index f6ad432..673c6ac 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -203,10 +203,10 @@ export default function LineBarChart(props) { const lineData = {}; const barData = {}; - nrqlData.forEach(d => { + (nrqlData || []).forEach(d => { const { data, color, name, type, barSize } = d; - data.forEach(groupData => { + (data || []).forEach(groupData => { const hasFacet = groupData.metadata.groups.find( g => g.type === 'facet' ); From 4f76ffee94ffd193d6f48170ee54765b22b00128 Mon Sep 17 00:00:00 2001 From: Kav91 Date: Tue, 5 Sep 2023 00:40:07 +1000 Subject: [PATCH 04/27] feat: custom docs --- .../nr-line-bar-chart/docs/index.js | 124 ++++++++++++++---- visualizations/nr-line-bar-chart/nr1.json | 6 +- 2 files changed, 100 insertions(+), 30 deletions(-) diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js index ede72ea..57e241b 100644 --- a/visualizations/nr-line-bar-chart/docs/index.js +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -5,43 +5,111 @@ import { CardHeader, CardBody, HeadingText, - Link - // NrqlQuery, - // Spinner, - // AutoSizer, - // BlockText, - // List, - // ListItem + Link, + BlockText, + Spacing } from 'nr1'; +const properties = require('../nr1.json'); + +// if the key matches a config property name, the additional information will be added +const additionalDocs = { + tickFormat: { + description: '', // optional override of the property config description + links: [ + { + name: 'Parsing documentation', + link: 'https://momentjs.com/docs/#/parsing/string-format/' + } + ] + } +}; + export default function Docs() { return (
- Documentation - - Example Line Queries - - SELECT count(*) as 'Views' from PageView where TIMESERIES 1 day since - last week + Documentation + + + + + In order to populate the chart, there are a few requirements: + +
    +
  • At least 1 bar query and 1 time query
  • +
  • + Each query must use the TIMESERIES clause, with + the same bucket eg. TIMESERIES 1 day +
  • +
  • + If using the SINCE clause it is strongly + recommended to be the same across all configured queries +
  • +
+
+ +
A valid bar query for the chart could look like this:
+
+ + + SELECT percentile(duration, 80) as 'Load' from PageView + TIMESERIES 1 day since last week + + + +
A valid line query for the chart could look like this:
+
+ + + SELECT count(*) as 'Views' from PageView where TIMESERIES 1 day + since last week + + +
- - Example Bar Queries - - SELECT percentile(duration, 80) as 'Load' from PageView TIMESERIES 1 - day since last week - - + + + + {properties.configuration + .filter(c => c.name !== 'showDocs') + .map(config => { + const { name, title, description } = config; + const extraDocs = additionalDocs[name]; + + return ( + + + {title} + + + {description || + extraDocs?.description || + 'No description provided.'} + + + + {extraDocs?.links && ( + <> + + Links + - - Tick Format - - The tick format is powered by moment. View their documentation for - other configuration options. - - Moment Date Parsing Documentation. - +
    + {extraDocs.links.map((l, i) => ( +
  • + {l.name} +
  • + ))} +
+ + )} +
+
+
+ ); + })}
diff --git a/visualizations/nr-line-bar-chart/nr1.json b/visualizations/nr-line-bar-chart/nr1.json index 7890fdf..d5572c1 100644 --- a/visualizations/nr-line-bar-chart/nr1.json +++ b/visualizations/nr-line-bar-chart/nr1.json @@ -13,13 +13,13 @@ { "name": "tickFormat", "title": "Moment Tick Formatter (default: YYYY-MM-DD)", - "description": "", + "description": "The tick format is powered by moment. View their documentation for other configuration options.", "type": "string" }, { "name": "pollInterval", "title": "Select Poll Interval (seconds) default 60s", - "description": "", + "description": "Configurable poll interval, default 1 minute ", "type": "enum", "items": [ { @@ -59,6 +59,7 @@ { "name": "barQueries", "title": "Bar Queries", + "description":"Collection of Bar Chart queries", "type": "collection", "items": [ { @@ -102,6 +103,7 @@ { "name": "lineQueries", "title": "Line Queries", + "description":"Collection of Line Chart queries", "type": "collection", "items": [ { From 02b0bf4d0b0da649cfb8f69168162473c030f317 Mon Sep 17 00:00:00 2001 From: Kav91 Date: Tue, 5 Sep 2023 00:43:09 +1000 Subject: [PATCH 05/27] feat: custom docs --- visualizations/nr-line-bar-chart/docs/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js index 57e241b..d60244f 100644 --- a/visualizations/nr-line-bar-chart/docs/index.js +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -16,6 +16,7 @@ const properties = require('../nr1.json'); const additionalDocs = { tickFormat: { description: '', // optional override of the property config description + additionalInfo: '', // optional extra info links: [ { name: 'Parsing documentation', @@ -89,6 +90,12 @@ export default function Docs() { 'No description provided.'} + {extraDocs?.additionalInfo && ( + + {extraDocs?.additionalInfo} + + )} + {extraDocs?.links && ( <> From c963affe6de9cbf6c9dec901bd0e39fac1242338 Mon Sep 17 00:00:00 2001 From: Kav91 Date: Mon, 11 Sep 2023 14:53:27 +1000 Subject: [PATCH 06/27] feat: update docs, barSize, colors --- package-lock.json | 5 +++++ package.json | 2 +- visualizations/nr-line-bar-chart/docs/index.js | 6 +++--- visualizations/nr-line-bar-chart/index.js | 15 ++++++++------- visualizations/nr-line-bar-chart/nr1.json | 4 ++-- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index e67bf5c..694e0d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -847,6 +847,11 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" }, + "dayjs": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", + "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 2ce9209..2f12f36 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "@mantine/hooks": "^5.8.4", "@newrelic/nr1-community": "latest", "chart.js": "^3.9.1", + "dayjs": "^1.11.9", "mapbox-gl": "^2.10.0", - "moment": "^2.29.4", "prop-types": "^15.7.2", "react-chartjs-2": "^4.3.1", "react-map-gl": "^7.0.19", diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js index d60244f..dd3e481 100644 --- a/visualizations/nr-line-bar-chart/docs/index.js +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -20,7 +20,7 @@ const additionalDocs = { links: [ { name: 'Parsing documentation', - link: 'https://momentjs.com/docs/#/parsing/string-format/' + link: 'https://day.js.org/docs/en/display/format' } ] } @@ -62,8 +62,8 @@ export default function Docs() { - SELECT count(*) as 'Views' from PageView where TIMESERIES 1 day - since last week + SELECT count(*) as 'Views' from PageView TIMESERIES 1 day since + last week diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index 673c6ac..3f12f7c 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -25,7 +25,7 @@ import { } from 'nr1'; import { useInterval } from '@mantine/hooks'; -import moment from 'moment'; +import dayjs from 'dayjs'; import Docs from './docs'; @@ -149,9 +149,9 @@ export default function LineBarChart(props) { const doNrql = data => { return new Promise(resolve => { - const { query, accountId, name, color, type } = data; + const { query, accountId, name, color, type, barSize } = data; NrqlQuery.query({ query, accountIds: [accountId] }).then(value => { - resolve({ ...value, name, color, type }); + resolve({ ...value, name, color, type, barSize }); }); }); }; @@ -210,7 +210,8 @@ export default function LineBarChart(props) { const hasFacet = groupData.metadata.groups.find( g => g.type === 'facet' ); - groupData.metadata.color = hasFacet ? groupData.metadata.color : color; + groupData.metadata.color = + hasFacet || !color ? groupData.metadata.color : color; const dataKey = groupData.metadata.groups.find( g => g.type === 'function' @@ -246,7 +247,7 @@ export default function LineBarChart(props) { if (!barData[`B:${baseName}`]) { barData[`B:${baseName}`] = { results: [entry], - barSize, + barSize: barSize > 0 ? barSize : 20, name, color: groupData.metadata.color }; @@ -294,7 +295,7 @@ export default function LineBarChart(props) { domain={['auto', 'auto']} name="Time" tickFormatter={unixTime => - moment(unixTime).format(tickFormat || 'YYYY-MM-DD') + dayjs(unixTime).format(tickFormat || 'YYYY-MM-DD') } type="number" /> @@ -308,7 +309,7 @@ export default function LineBarChart(props) { ))} diff --git a/visualizations/nr-line-bar-chart/nr1.json b/visualizations/nr-line-bar-chart/nr1.json index d5572c1..f8e2ce3 100644 --- a/visualizations/nr-line-bar-chart/nr1.json +++ b/visualizations/nr-line-bar-chart/nr1.json @@ -12,8 +12,8 @@ }, { "name": "tickFormat", - "title": "Moment Tick Formatter (default: YYYY-MM-DD)", - "description": "The tick format is powered by moment. View their documentation for other configuration options.", + "title": "DayJS Tick Formatter (default: YYYY-MM-DD)", + "description": "The tick format is powered by dayjs. View their documentation for other configuration options.", "type": "string" }, { From 20c5379c8854579b32ba60bd57761f1232c31788 Mon Sep 17 00:00:00 2001 From: Anna Strom-Olsen Date: Mon, 11 Sep 2023 16:07:28 -0400 Subject: [PATCH 07/27] feat: update error state css - Moved error block to the top (ahead of docs), so user won't miss it if docs are toggled on - Isolated the docs block overall into its own class to avoid collisions with nr1-community css - Updated styling for the error block --- visualizations/nr-line-bar-chart/index.js | 94 ++++++++++++-------- visualizations/nr-line-bar-chart/styles.scss | 21 +++++ 2 files changed, 78 insertions(+), 37 deletions(-) diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index 3f12f7c..03e5c56 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -88,7 +88,7 @@ export default function LineBarChart(props) { errorObj.errors.push(`Query is undefined`); } if (!lowerQuery.includes('timeseries')) { - errorObj.errors.push(`Should contain TIMESERIES keyword`); + errorObj.errors.push(`Requires TIMESERIES keyword`); } if (!accountId) { errorObj.errors.push(`AccountID is undefined`); @@ -117,7 +117,7 @@ export default function LineBarChart(props) { errorObj.errors.push(`Query is undefined`); } if (!lowerQuery.includes('timeseries')) { - errorObj.errors.push(`Should contain TIMESERIES keyword`); + errorObj.errors.push(`Requires TIMESERIES keyword`); } if (!accountId) { errorObj.errors.push(`AccountID is undefined`); @@ -164,8 +164,9 @@ export default function LineBarChart(props) { const { accountId, query, enableFilters, color, name } = q; /* eslint-disable */ - const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${enableTimePicker ? timeQuery : '' - }`; + const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${ + enableTimePicker ? timeQuery : '' + }`; /* eslint-enable */ queries.push({ query: newQuery, accountId, type: 'line', color, name }); @@ -177,8 +178,9 @@ export default function LineBarChart(props) { const { accountId, query, enableFilters, color, name, barSize } = q; /* eslint-disable */ - const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${enableTimePicker ? timeQuery : '' - }`; + const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${ + enableTimePicker ? timeQuery : '' + }`; /* eslint-enable */ queries.push({ @@ -226,8 +228,7 @@ export default function LineBarChart(props) { /* eslint-disable */ const entry = { time: gd.begin_time, - [`${type === 'line' ? 'L' : 'B'}:${baseName - }`]: value + [`${type === 'line' ? 'L' : 'B'}:${baseName}`]: value }; /* eslint-enable */ @@ -332,37 +333,56 @@ export default function LineBarChart(props) { } const ErrorState = (errors, showDocs) => ( - + +
+ + Sorry, there was a problem! + + + + Enable the documentation toggle in the edit settings for more help. + + + {(errors || []).map((err, idx) => ( +
+ + {err.name} + + + + {(err?.errors || []).map((err, i) => ( + + {err} + + ))} + + +
+ ))} +
{showDocs && } - - - Oops! Something went wrong. - - - - Enable the documentation toggle in the edit settings for more help. - - -
- - {(errors || []).map(err => ( - <> - {err.name} - - - {(err?.errors || []).map((err, i) => ( - {err} - ))} - - -
- - ))}
); diff --git a/visualizations/nr-line-bar-chart/styles.scss b/visualizations/nr-line-bar-chart/styles.scss index e69de29..e7e5c84 100644 --- a/visualizations/nr-line-bar-chart/styles.scss +++ b/visualizations/nr-line-bar-chart/styles.scss @@ -0,0 +1,21 @@ +.DocState { + text-align: left; + + .ErrorState { + &-errors { + margin-bottom: 24px; + } + + &-cardBody { + height: 100%; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + } + + &-errorBody { + margin: 0 12px 8px 12px; + } + } +} From d4e9c6fe31dbe413a84d831f4b100510ba4cfc52 Mon Sep 17 00:00:00 2001 From: Kav91 Date: Wed, 13 Sep 2023 14:19:08 +1000 Subject: [PATCH 08/27] feat: enable time picker + docs update --- README.md | 1 + visualizations/nr-line-bar-chart/docs/index.js | 14 +++++++++----- visualizations/nr-line-bar-chart/index.js | 12 ++++-------- visualizations/nr-line-bar-chart/nr1.json | 8 +++++++- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a69a524..f1fe5e1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A collection of custom visualizations to enhance your dashboarding experience. - Multi Line Compare - Multi Line & Event Overlay Chart - Scatter & Event Overlay Chart +- Line & Bar Chart - Area & Event Overlay Chart - Radar Chart w/ChartJS - Action Loader diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js index dd3e481..3441b06 100644 --- a/visualizations/nr-line-bar-chart/docs/index.js +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -99,17 +99,21 @@ export default function Docs() { {extraDocs?.links && ( <> - + Links -
    + {extraDocs.links.map((l, i) => ( -
  • + {l.name} -
  • +
    +
    ))} -
+
)} diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index 03e5c56..4e1ef48 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -164,9 +164,8 @@ export default function LineBarChart(props) { const { accountId, query, enableFilters, color, name } = q; /* eslint-disable */ - const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${ - enableTimePicker ? timeQuery : '' - }`; + const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${enableTimePicker ? timeQuery : '' + }`; /* eslint-enable */ queries.push({ query: newQuery, accountId, type: 'line', color, name }); @@ -178,9 +177,8 @@ export default function LineBarChart(props) { const { accountId, query, enableFilters, color, name, barSize } = q; /* eslint-disable */ - const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${ - enableTimePicker ? timeQuery : '' - }`; + const newQuery = `${query} ${enableFilters ? filters || '' : ''} ${enableTimePicker ? timeQuery : '' + }`; /* eslint-enable */ queries.push({ @@ -225,12 +223,10 @@ export default function LineBarChart(props) { const baseName = `${name} - ${groupData.metadata.name}`; - /* eslint-disable */ const entry = { time: gd.begin_time, [`${type === 'line' ? 'L' : 'B'}:${baseName}`]: value }; - /* eslint-enable */ finalData.push(entry); diff --git a/visualizations/nr-line-bar-chart/nr1.json b/visualizations/nr-line-bar-chart/nr1.json index f8e2ce3..f431200 100644 --- a/visualizations/nr-line-bar-chart/nr1.json +++ b/visualizations/nr-line-bar-chart/nr1.json @@ -10,9 +10,15 @@ "description": "", "type": "boolean" }, + { + "name": "enableTimePicker", + "title": "Enable Platform Time Picker", + "description": "", + "type": "boolean" + }, { "name": "tickFormat", - "title": "DayJS Tick Formatter (default: YYYY-MM-DD)", + "title": "Date Formatter (default: YYYY-MM-DD)", "description": "The tick format is powered by dayjs. View their documentation for other configuration options.", "type": "string" }, From a68cabe30ca40ec86a5d1efd910fe9520816a03d Mon Sep 17 00:00:00 2001 From: Kav91 Date: Wed, 13 Sep 2023 14:46:25 +1000 Subject: [PATCH 09/27] feat: rework chart data handling --- package-lock.json | 5 --- .../nr-line-bar-chart/docs/index.js | 4 ++ visualizations/nr-line-bar-chart/index.js | 41 +++++++++++-------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 694e0d8..598b9cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2290,11 +2290,6 @@ "minimist": "^1.2.6" } }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js index 3441b06..ec624d9 100644 --- a/visualizations/nr-line-bar-chart/docs/index.js +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -46,6 +46,10 @@ export default function Docs() { If using the SINCE clause it is strongly recommended to be the same across all configured queries +
  • + Only use unique names for your line and bar queries to avoid + issues. +
  • diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index 4e1ef48..b658339 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -199,9 +199,9 @@ export default function LineBarChart(props) { const nrqlData = await Promise.all(queries.map(q => doNrql(q))); // perform data merging - const finalData = []; const lineData = {}; const barData = {}; + const timeGroups = {}; (nrqlData || []).forEach(d => { const { data, color, name, type, barSize } = d; @@ -222,34 +222,45 @@ export default function LineBarChart(props) { const value = gd[dataKey]; const baseName = `${name} - ${groupData.metadata.name}`; + const timeGroup = dayjs(gd.begin_time).format( + tickFormat || 'YYYY-MM-DD' + ); + + if (!timeGroups[timeGroup]) { + timeGroups[timeGroup] = { + timeGroup, + [`${baseName}`]: value + }; + } else { + timeGroups[timeGroup][`${baseName}`] = value; + } const entry = { time: gd.begin_time, - [`${type === 'line' ? 'L' : 'B'}:${baseName}`]: value + timeGroup, + [`${baseName}`]: value }; - finalData.push(entry); - if (type === 'line') { - if (!lineData[`L:${baseName}`]) { - lineData[`L:${baseName}`] = { + if (!lineData[`${baseName}`]) { + lineData[`${baseName}`] = { results: [entry], name, color: groupData.metadata.color }; } else { - lineData[`L:${baseName}`].results.push(entry); + lineData[`${baseName}`].results.push(entry); } } else if (type === 'bar') { - if (!barData[`B:${baseName}`]) { - barData[`B:${baseName}`] = { + if (!barData[`${baseName}`]) { + barData[`${baseName}`] = { results: [entry], barSize: barSize > 0 ? barSize : 20, name, color: groupData.metadata.color }; } else { - barData[`B:${baseName}`].results.push(entry); + barData[`${baseName}`].results.push(entry); } } } @@ -259,7 +270,7 @@ export default function LineBarChart(props) { setBarData(barData); setLineData(lineData); - setDataSets(finalData); + setDataSets(Object.keys(timeGroups).map(tg => timeGroups[tg])); }; if (loading) { @@ -288,15 +299,11 @@ export default function LineBarChart(props) { > - dayjs(unixTime).format(tickFormat || 'YYYY-MM-DD') - } - type="number" + type="category" /> - {/* */} From f6235a2cf694a8c7f9c5a729b35d74244e7ee1a4 Mon Sep 17 00:00:00 2001 From: Kav91 Date: Thu, 14 Sep 2023 22:06:38 +1000 Subject: [PATCH 10/27] feat: add x/y label and nested docs --- .../nr-line-bar-chart/docs/index.js | 109 +++++++++++------- visualizations/nr-line-bar-chart/index.js | 23 +++- visualizations/nr-line-bar-chart/nr1.json | 12 ++ 3 files changed, 96 insertions(+), 48 deletions(-) diff --git a/visualizations/nr-line-bar-chart/docs/index.js b/visualizations/nr-line-bar-chart/docs/index.js index ec624d9..a29dec6 100644 --- a/visualizations/nr-line-bar-chart/docs/index.js +++ b/visualizations/nr-line-bar-chart/docs/index.js @@ -26,6 +26,70 @@ const additionalDocs = { } }; +const renderNr1Props = (config, isNested) => { + const { name, title, description, type, items } = config; + const extraDocs = additionalDocs[name]; + + if (type === 'collection') { + return ( +
    + {title} + + {description || extraDocs?.description || 'No description provided.'} + + + {items.map(item => { + return renderNr1Props(item, true); + })} +
    + ); + } + + return ( +
    + + {title} + + + {description || extraDocs?.description || 'No description provided.'} + + + {extraDocs?.additionalInfo && ( + + {extraDocs?.additionalInfo} + + )} + + + {extraDocs?.links && ( + <> + + Links + + + + {extraDocs.links.map((l, i) => ( + + {l.name} +
    +
    + ))} +
    + + )} +
    +
    +
    + ); +}; + export default function Docs() { return (
    @@ -80,50 +144,7 @@ export default function Docs() { {properties.configuration .filter(c => c.name !== 'showDocs') .map(config => { - const { name, title, description } = config; - const extraDocs = additionalDocs[name]; - - return ( - - - {title} - - - {description || - extraDocs?.description || - 'No description provided.'} - - - {extraDocs?.additionalInfo && ( - - {extraDocs?.additionalInfo} - - )} - - - {extraDocs?.links && ( - <> - - Links - - - - {extraDocs.links.map((l, i) => ( - - {l.name} -
    -
    - ))} -
    - - )} -
    -
    -
    - ); + return renderNr1Props(config); })} diff --git a/visualizations/nr-line-bar-chart/index.js b/visualizations/nr-line-bar-chart/index.js index b658339..4c574ed 100644 --- a/visualizations/nr-line-bar-chart/index.js +++ b/visualizations/nr-line-bar-chart/index.js @@ -7,7 +7,8 @@ import { YAxis, CartesianGrid, Tooltip, - Legend + Legend, + Label } from 'recharts'; import { @@ -58,7 +59,9 @@ export default function LineBarChart(props) { lineQueries, barQueries, showDocs, - tickFormat + tickFormat, + yLabelRight, + xLabel } = props; const [errors, setErrors] = useState([]); const [dataSets, setDataSets] = useState([]); @@ -303,9 +306,21 @@ export default function LineBarChart(props) { domain={['auto', 'auto']} name="Time" type="category" - /> + > + {xLabel && ( +