From 04acd15b8fd8ce7d74883fcbb0d6ed30d5060c7c Mon Sep 17 00:00:00 2001 From: Iuliia Kulagina <86924383+kullJul@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:21:10 +0200 Subject: [PATCH] Interactivity removal + context menu fix (#104) * Replace interactivity service with selectionManager * Add aria labels * Add tests for selection and keyboard navigation * Increment visual version * Added interactivity to legend * Update dependencies --------- Co-authored-by: Iuliia Kulagina --- CHANGELOG.md | 7 + karma.conf.ts | 2 +- package-lock.json | 703 +++++++++++++++++++----------------- package.json | 36 +- pbiviz.json | 6 +- src/TornadoChart.ts | 152 +++----- src/TornadoWebBehavior.ts | 222 ++++++++---- src/interfaces.ts | 20 +- src/tornadoChartUtils.ts | 30 +- style/tornadoChart.less | 1 - test/TornadoChartBuilder.ts | 23 +- test/visualTest.ts | 186 +++++++++- 12 files changed, 844 insertions(+), 544 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca6b163..410bdf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.0.4.0 +* Use selectionManager instead of interactivity utils +* Add interactivity to legend +* Add context menu to legend and axis labels +* Add aria-labels +* API 5.11.10 + ## 3.0.3.0 * Fix issue with categoryAxis end type * Update packages diff --git a/karma.conf.ts b/karma.conf.ts index 9686e8c..778eb5d 100644 --- a/karma.conf.ts +++ b/karma.conf.ts @@ -34,7 +34,7 @@ const testRecursivePath = "test/visualTest.ts"; const srcOriginalRecursivePath = "src/**/*.ts"; const coverageFolder = "coverage"; -require("playwright").chromium.executablePath(); +process.env.CHROME_BIN = require("playwright-chromium").chromium.executablePath(); module.exports = (config) => { config.set({ diff --git a/package-lock.json b/package-lock.json index a51a908..705bc16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,36 +1,42 @@ { "name": "powerbi-visuals-tornadochart", - "version": "3.0.3.0", + "version": "3.0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "powerbi-visuals-tornadochart", - "version": "3.0.3.0", + "version": "3.0.4.0", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.9.1", "coverage-istanbul-loader": "^3.0.5", "d3-array": "^3.2.4", "d3-brush": "^3.0.0", "d3-selection": "^3.0.0", - "eslint": "^8.53.0", + "eslint": "^8.57.0", "eslint-plugin-powerbi-visuals": "^0.8.1", - "playwright": "^1.39.0", - "powerbi-visuals-utils-formattingmodel": "^6.0.0" + "powerbi-visuals-api": "^5.11.0", + "powerbi-visuals-utils-chartutils": "7.0.0-beta.1", + "powerbi-visuals-utils-colorutils": "6.0.4", + "powerbi-visuals-utils-dataviewutils": "6.1.0", + "powerbi-visuals-utils-formattingmodel": "^6.0.0", + "powerbi-visuals-utils-formattingutils": "6.1.1", + "powerbi-visuals-utils-svgutils": "6.0.4", + "powerbi-visuals-utils-tooltiputils": "6.0.4", + "powerbi-visuals-utils-typeutils": "6.0.3" }, "devDependencies": { - "@types/d3": "^7.4.2", + "@types/d3": "^7.4.3", "@types/jasmine": "5.1.1", "@types/jasmine-jquery": "1.5.36", "@types/jquery": "3.5.25", "@types/karma": "6.3.6", "@types/webpack": "5.28.4", - "ajv": "^8.12.0", "css-loader": "6.8.1", "jasmine": "5.1.0", - "karma": "^6.4.2", + "karma": "^6.4.3", "karma-chrome-launcher": "3.2.0", "karma-coverage": "2.2.1", "karma-coverage-istanbul-reporter": "^3.0.3", @@ -42,22 +48,14 @@ "karma-webpack": "5.0.0", "less": "^4.2.0", "less-loader": "^11.1.3", - "powerbi-visuals-api": "^5.9.1", - "powerbi-visuals-tools": "^5.4.3", - "powerbi-visuals-utils-chartutils": "6.0.4", - "powerbi-visuals-utils-colorutils": "6.0.4", - "powerbi-visuals-utils-dataviewutils": "6.1.0", - "powerbi-visuals-utils-formattingutils": "6.1.1", - "powerbi-visuals-utils-interactivityutils": "6.0.4", - "powerbi-visuals-utils-svgutils": "6.0.4", + "playwright-chromium": "^1.45.2", + "powerbi-visuals-tools": "^5.5.1", "powerbi-visuals-utils-testutils": "6.1.1", - "powerbi-visuals-utils-tooltiputils": "6.0.4", - "powerbi-visuals-utils-typeutils": "6.0.3", "style-loader": "3.3.3", "stylelint": "^15.11.0", "ts-loader": "9.5.0", "typescript": "5.2.2", - "webpack": "5.89.0" + "webpack": "^5.89.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -532,9 +530,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -568,11 +566,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -583,9 +576,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -599,9 +592,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -612,17 +605,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -645,20 +627,21 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -675,9 +658,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -719,9 +702,10 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -999,9 +983,9 @@ } }, "node_modules/@types/d3": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.2.tgz", - "integrity": "sha512-Y4g2Yb30ZJmmtqAJTqMRaqXwRawfvpdpVmyEYEcyGNhrQI/Zvkq3k7yE1tdN07aFSmNBfvmegMQ9Fe2qy9ZMhw==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", "dev": true, "dependencies": { "@types/d3-array": "*", @@ -1417,9 +1401,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==" + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" }, "node_modules/@types/send": { "version": "0.17.4", @@ -1487,15 +1471,15 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", - "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/type-utils": "6.9.1", - "@typescript-eslint/utils": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1568,77 +1552,6 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/parser/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1661,12 +1574,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", - "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1677,12 +1590,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", - "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dependencies": { - "@typescript-eslint/typescript-estree": "6.9.1", - "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1703,9 +1616,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -1724,9 +1637,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/types": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", - "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -1736,15 +1649,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", - "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", + "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, @@ -1762,9 +1676,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -1783,16 +1697,16 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/@typescript-eslint/utils": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", - "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -1807,11 +1721,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", - "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dependencies": { - "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2140,6 +2054,11 @@ "node": ">= 8" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -2348,11 +2267,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3018,24 +2937,6 @@ } } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/coverage-istanbul-loader": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", @@ -3206,7 +3107,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "dev": true, "engines": { "node": ">=12" } @@ -3266,7 +3166,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "dev": true, "engines": { "node": ">=12" } @@ -3286,7 +3185,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dev": true, "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", @@ -3310,7 +3208,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "dev": true, "dependencies": { "d3-array": "2 - 3" }, @@ -3322,7 +3219,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dev": true, "dependencies": { "d3-time": "1 - 3" }, @@ -3667,9 +3563,9 @@ } }, "node_modules/engine.io": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.3.tgz", - "integrity": "sha512-IML/R4eG/pUS5w7OfcDE0jKrljWS9nwnEfsxWCIJF5eO6AHo6+Hlv+lQbdlAYsiJPHzUthLm1RUjnBzWOs45cw==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -3681,7 +3577,7 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" + "ws": "~8.17.1" }, "engines": { "node": ">=10.2.0" @@ -3720,16 +3616,16 @@ "dev": true }, "node_modules/engine.io/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -3810,15 +3706,15 @@ } }, "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -3939,11 +3835,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4044,17 +3935,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4497,9 +4377,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4662,7 +4542,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -5748,6 +5627,17 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -5859,9 +5749,9 @@ } }, "node_modules/karma": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.2.tgz", - "integrity": "sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", + "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", "dev": true, "dependencies": { "@colors/colors": "1.5.0", @@ -5883,7 +5773,7 @@ "qjobs": "^1.2.0", "range-parser": "^1.2.1", "rimraf": "^3.0.2", - "socket.io": "^4.4.1", + "socket.io": "^4.7.2", "source-map": "^0.6.1", "tmp": "^0.2.1", "ua-parser-js": "^0.7.30", @@ -7495,45 +7385,32 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "node_modules/playwright-chromium": { + "version": "1.45.2", + "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.45.2.tgz", + "integrity": "sha512-9p7UKy+Nm7mozk/5uk6f1SoDMqrrphYLheVPS2LhYQvep9pRjJJEVpGAO0ULN3uFuxnWA7Y2lbaDxFJykeavHA==", + "dev": true, + "hasInstallScript": true, "dependencies": { - "playwright-core": "1.39.0" + "playwright-core": "1.45.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "fsevents": "2.3.2" + "node": ">=18" } }, - "node_modules/playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "node_modules/playwright-chromium/node_modules/playwright-core": { + "version": "1.45.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.2.tgz", + "integrity": "sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==", + "dev": true, "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=18" } }, "node_modules/postcss": { @@ -7664,27 +7541,21 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/powerbi-models": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/powerbi-models/-/powerbi-models-1.13.0.tgz", - "integrity": "sha512-fToQmRqECBJSlHaKNAzFql52ryNnhSm2UwRXfsctcS5Hp//o9sExasVsASv6jZjXE8ACNyKjDUKdGqWsCjRd1Q==", - "dev": true - }, "node_modules/powerbi-visuals-api": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.9.1.tgz", - "integrity": "sha512-MaU7xY0gSBx8W7bb8HPzkS7mCnflNv1Yleh97ivB6cipYM5yD4kKrBPYgbmWUkPF/GzFIKzPzeKytoUoxicMPA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.11.0.tgz", + "integrity": "sha512-OWvhexc4Dqz4swiQiZLnGj+NWUoX84metePeRw5Or3wG3lN/0x6Yd3xqJwBH67uYyjqQ/eomuF7lvbryxCqTbQ==", "dependencies": { "semver": "^7.6.0" } }, "node_modules/powerbi-visuals-tools": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.4.3.tgz", - "integrity": "sha512-msriNFlq+jk+YfJ7lBOG657QJggFCD3h0YMmmk2H5F7M4doVoVuY/79jcs3MacjZds3lMARKexxR+V6n/tiwgA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.5.1.tgz", + "integrity": "sha512-330nPyn5KEssbVmYnoKHU0DiyUzxeZrw2tYIo1kaWUqkF7r6CUJuUTy/UNO8t6GGjgaYK6ieBE3ttRJO81nQ8Q==", "dev": true, "dependencies": { - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/parser": "^7.12.0", "assert": "^2.1.0", "async": "^3.2.5", "browserify-zlib": "^0.2.0", @@ -7695,7 +7566,7 @@ "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", - "css-loader": "^6.10.0", + "css-loader": "^6.11.0", "domain-browser": "^5.7.0", "events": "^3.3.0", "extra-watch-webpack-plugin": "^1.0.3", @@ -7705,12 +7576,12 @@ "json-loader": "0.5.7", "jszip": "^3.10.1", "less": "^4.2.0", - "less-loader": "^11.1.4", + "less-loader": "^12.2.0", "lodash.clonedeep": "4.5.0", "lodash.defaults": "4.2.0", "lodash.isequal": "4.5.0", "lodash.ismatch": "^4.4.0", - "mini-css-extract-plugin": "^2.8.1", + "mini-css-extract-plugin": "^2.9.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "powerbi-visuals-webpack-plugin": "4.1.0", @@ -7730,7 +7601,7 @@ "util": "^0.12.5", "vm-browserify": "^1.1.2", "webpack": "^5.91.0", - "webpack-bundle-analyzer": "4.10.1", + "webpack-bundle-analyzer": "4.10.2", "webpack-dev-server": "^4.15.2" }, "bin": { @@ -7743,6 +7614,109 @@ "fsevents": "*" } }, + "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/parser": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.1.tgz", + "integrity": "sha512-u+1Qx86jfGQ5i4JjK33/FnawZRpsLxRnKzGE6EABZ40KxVT/vWsiZFEBBHjFOljmmV3MBYOHEKi0Jm9hbAOClA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.16.1", + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/typescript-estree": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/scope-manager": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.1.tgz", + "integrity": "sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/types": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.1.tgz", + "integrity": "sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.1.tgz", + "integrity": "sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.1", + "@typescript-eslint/visitor-keys": "7.16.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.1.tgz", + "integrity": "sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.16.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/powerbi-visuals-tools/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -7872,6 +7846,23 @@ } } }, + "node_modules/powerbi-visuals-tools/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/powerbi-visuals-tools/node_modules/domain-browser": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-5.7.0.tgz", @@ -7912,6 +7903,53 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/powerbi-visuals-tools/node_modules/less-loader": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", + "dev": true, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/powerbi-visuals-tools/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/powerbi-visuals-tools/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/powerbi-visuals-tools/node_modules/readable-stream": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", @@ -8064,10 +8102,9 @@ } }, "node_modules/powerbi-visuals-utils-chartutils": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-chartutils/-/powerbi-visuals-utils-chartutils-6.0.4.tgz", - "integrity": "sha512-N2WD47EtOIVUBr5LEjw9IPEbzlN4eLlry2uUhWSdv3THV2Sp7GHvFf9GkquO3ag8jl1uqRqsmMdeE+/96nX7mw==", - "dev": true, + "version": "7.0.0-beta.1", + "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-chartutils/-/powerbi-visuals-utils-chartutils-7.0.0-beta.1.tgz", + "integrity": "sha512-f9WyA1/M83YiXLpgxI9SiKfmKKxullzlNcMHf4eznb8aGRcHeeZbDobF1k1kS5TUrOgBjqcTFAoYXn7cFw0N8A==", "dependencies": { "d3-array": "^3.2.4", "d3-axis": "^3.0.0", @@ -8075,7 +8112,6 @@ "d3-selection": "^3.0.0", "d3-transition": "^3.0.1", "powerbi-visuals-utils-formattingutils": "^6.1.1", - "powerbi-visuals-utils-interactivityutils": "^6.0.4", "powerbi-visuals-utils-svgutils": "^6.0.4", "powerbi-visuals-utils-typeutils": "^6.0.3" }, @@ -8087,7 +8123,6 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-colorutils/-/powerbi-visuals-utils-colorutils-6.0.4.tgz", "integrity": "sha512-gCQOU6KvY56zz9f/FQLns+01X90p7W+xyX3BbnH3ACR6/iWvrtCuzbGHrfSO/UVPN3vACVsG3zOabNVi43qm9Q==", - "dev": true, "dependencies": { "powerbi-visuals-utils-dataviewutils": "^6.0.3", "powerbi-visuals-utils-typeutils": "^6.0.3" @@ -8097,7 +8132,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-dataviewutils/-/powerbi-visuals-utils-dataviewutils-6.1.0.tgz", "integrity": "sha512-m/pcpaB5thXdwCSg+AKyaJxVibCPmwC5ezsgccoI0H5jpsvu78lxFRTf+Tzoi1IAbLaWHyJAfxJ0F56jmioTOg==", - "dev": true, "optionalDependencies": { "fsevents": "*" } @@ -8114,7 +8148,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-formattingutils/-/powerbi-visuals-utils-formattingutils-6.1.1.tgz", "integrity": "sha512-KQNjQQIfH007COcr5xMdN9WVZ6v2OcYX7l4U8ZL5lTpcKhaQBH3g96AUECz3oWnK7rmqI9DjqdikUTkcPBgo5Q==", - "dev": true, "dependencies": { "powerbi-visuals-api": "^5.9.0", "powerbi-visuals-utils-dataviewutils": "^6.0.3", @@ -8124,23 +8157,10 @@ "fsevents": "2.3.3" } }, - "node_modules/powerbi-visuals-utils-interactivityutils": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-interactivityutils/-/powerbi-visuals-utils-interactivityutils-6.0.4.tgz", - "integrity": "sha512-oqC3juH9gc+oYhWX7dijQWD7rf2Lgc4Hi/G0JNvPa/jbOdS55jgaUyPpknRlRl4RU+lcAa1SXk1146a6+a+5gw==", - "dev": true, - "dependencies": { - "d3-selection": "^3.0.0", - "powerbi-models": "1.13.0", - "powerbi-visuals-utils-svgutils": "^6.0.4", - "powerbi-visuals-utils-typeutils": "^6.0.3" - } - }, "node_modules/powerbi-visuals-utils-svgutils": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-svgutils/-/powerbi-visuals-utils-svgutils-6.0.4.tgz", "integrity": "sha512-5aS/nxvO25adfmUn9bLh1OMmbQFGzN7b1AETp2ak/mtpMJ3dTRuXpq1wyXpByxIYTkrQ7iPsrUtdjCSXeboX2Q==", - "dev": true, "dependencies": { "d3-selection": "^3.0.0", "d3-timer": "^3.0.1", @@ -8168,7 +8188,6 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-tooltiputils/-/powerbi-visuals-utils-tooltiputils-6.0.4.tgz", "integrity": "sha512-FK1sHuw0LKyjpfP1b3TA7uwNnjuKRW4PTFx6aEzhnp42TgZhzFP/VP1qKFAsrpqDrYNKA/gFElT11owtXFx/QA==", - "dev": true, "dependencies": { "d3-selection": "^3.0.0" } @@ -8177,7 +8196,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/powerbi-visuals-utils-typeutils/-/powerbi-visuals-utils-typeutils-6.0.3.tgz", "integrity": "sha512-GKJTdIgoNushNHaFOHkyRIdccbnk4ECb89DqVICthm3fuLHOD15Nb3hH45kz+2dr8lPdc9oCHhuDiBGMcCr1UQ==", - "dev": true, "optionalDependencies": { "fsevents": "*" } @@ -9086,25 +9104,49 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "dependencies": { - "ws": "~8.11.0" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -10252,11 +10294,11 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -10674,9 +10716,9 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz", - "integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "0.5.7", @@ -10687,7 +10729,6 @@ "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", - "is-plain-object": "^5.0.0", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", @@ -10842,9 +10883,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "engines": { "node": ">=10.0.0" @@ -11138,9 +11179,9 @@ } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/package.json b/package.json index 6eef612..fe08911 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "powerbi-visuals-tornadochart", - "version": "3.0.3.0", + "version": "3.0.4.0", "author": { "name": "Microsoft", "email": "pbicvsupport@microsoft.com" @@ -20,16 +20,15 @@ "url": "git+https://github.com/Microsoft/PowerBI-visuals-Tornado.git" }, "devDependencies": { - "@types/d3": "^7.4.2", + "@types/d3": "^7.4.3", "@types/jasmine": "5.1.1", "@types/jasmine-jquery": "1.5.36", "@types/jquery": "3.5.25", "@types/karma": "6.3.6", "@types/webpack": "5.28.4", - "ajv": "^8.12.0", "css-loader": "6.8.1", "jasmine": "5.1.0", - "karma": "^6.4.2", + "karma": "^6.4.3", "karma-chrome-launcher": "3.2.0", "karma-coverage": "2.2.1", "karma-coverage-istanbul-reporter": "^3.0.3", @@ -41,33 +40,32 @@ "karma-webpack": "5.0.0", "less": "^4.2.0", "less-loader": "^11.1.3", - "powerbi-visuals-api": "^5.9.1", - "powerbi-visuals-tools": "^5.4.3", - "powerbi-visuals-utils-chartutils": "6.0.4", - "powerbi-visuals-utils-colorutils": "6.0.4", - "powerbi-visuals-utils-dataviewutils": "6.1.0", - "powerbi-visuals-utils-formattingutils": "6.1.1", - "powerbi-visuals-utils-interactivityutils": "6.0.4", - "powerbi-visuals-utils-svgutils": "6.0.4", + "playwright-chromium": "^1.45.2", + "powerbi-visuals-tools": "^5.5.1", "powerbi-visuals-utils-testutils": "6.1.1", - "powerbi-visuals-utils-tooltiputils": "6.0.4", - "powerbi-visuals-utils-typeutils": "6.0.3", "style-loader": "3.3.3", "stylelint": "^15.11.0", "ts-loader": "9.5.0", "typescript": "5.2.2", - "webpack": "5.89.0" + "webpack": "^5.89.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.9.1", "coverage-istanbul-loader": "^3.0.5", "d3-array": "^3.2.4", "d3-brush": "^3.0.0", "d3-selection": "^3.0.0", - "eslint": "^8.53.0", + "eslint": "^8.57.0", "eslint-plugin-powerbi-visuals": "^0.8.1", - "playwright": "^1.39.0", - "powerbi-visuals-utils-formattingmodel": "^6.0.0" + "powerbi-visuals-api": "^5.11.0", + "powerbi-visuals-utils-chartutils": "7.0.0-beta.1", + "powerbi-visuals-utils-colorutils": "6.0.4", + "powerbi-visuals-utils-dataviewutils": "6.1.0", + "powerbi-visuals-utils-formattingmodel": "^6.0.0", + "powerbi-visuals-utils-formattingutils": "6.1.1", + "powerbi-visuals-utils-svgutils": "6.0.4", + "powerbi-visuals-utils-tooltiputils": "6.0.4", + "powerbi-visuals-utils-typeutils": "6.0.3" } } diff --git a/pbiviz.json b/pbiviz.json index 8ff9609..53e2438 100644 --- a/pbiviz.json +++ b/pbiviz.json @@ -1,15 +1,15 @@ { "visual": { "name": "TornadoChart", - "displayName": "Tornado 3.0.3.0", + "displayName": "Tornado 3.0.4.0", "guid": "TornadoChart1452517688218", "visualClassName": "TornadoChart", - "version": "3.0.3.0", + "version": "3.0.4.0", "description": "A bar chart with category values listed vertically. Use for comparing the relative importance of a variable between two distinct groups.", "supportUrl": "https://community.powerbi.com", "gitHubUrl": "https://github.com/Microsoft/PowerBI-visuals-Tornado" }, - "apiVersion": "5.9.1", + "apiVersion": "5.11.0", "author": { "name": "Microsoft", "email": "pbicvsupport@microsoft.com" diff --git a/src/TornadoChart.ts b/src/TornadoChart.ts index 31e7e73..222f7df 100644 --- a/src/TornadoChart.ts +++ b/src/TornadoChart.ts @@ -58,7 +58,6 @@ import VisualConstructorOptions = powerbiVisualsApi.extensibility.visual.VisualC import IVisual = powerbiVisualsApi.extensibility.visual.IVisual; import IVisualHost = powerbiVisualsApi.extensibility.visual.IVisualHost; -import ISelectionManager = powerbiVisualsApi.extensibility.ISelectionManager; import ISelectionId = powerbiVisualsApi.visuals.ISelectionId; import { dataViewObjects } from "powerbi-visuals-utils-dataviewutils"; @@ -73,7 +72,7 @@ import translateAndRotate = manipulation.translateAndRotate; import { pixelConverter as PixelConverter } from "powerbi-visuals-utils-typeutils"; -import { legend as LegendModule, legendInterfaces, legendData, dataLabelUtils, OpacityLegendBehavior } from "powerbi-visuals-utils-chartutils"; +import { legend as LegendModule, legendInterfaces, legendData, dataLabelUtils } from "powerbi-visuals-utils-chartutils"; import ILegend = legendInterfaces.ILegend; import MarkerShape = legendInterfaces.MarkerShape; import LegendPosition = legendInterfaces.LegendPosition; @@ -86,18 +85,6 @@ import { textMeasurementService , valueFormatter } from "powerbi-visuals-utils-f import { TextProperties } from "powerbi-visuals-utils-formattingutils/lib/src/interfaces"; import IValueFormatter = valueFormatter.IValueFormatter; -import { - interactivitySelectionService as interactivityService, - interactivityBaseService -} from "powerbi-visuals-utils-interactivityutils"; -import appendClearCatcher = interactivityBaseService.appendClearCatcher; -import SelectableDataPoint = interactivityService.SelectableDataPoint; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; -import IInteractivityService = interactivityBaseService.IInteractivityService; -import createInteractivitySelectionService = interactivityService.createInteractivitySelectionService; - -type IInteractivityServiceSelectable = IInteractivityService; - import { ColorHelper } from "powerbi-visuals-utils-colorutils"; // powerbi.extensibility.utils.formattingModel import { FormattingSettingsService } from "powerbi-visuals-utils-formattingmodel"; @@ -115,13 +102,14 @@ import { } from "./interfaces"; import { TornadoWebBehavior } from "./TornadoWebBehavior"; import * as tooltipBuilder from "./tooltipBuilder"; -import { TornadoChartUtils } from "./tornadoChartUtils"; import { TornadoChartSettingsModel, DataLabelSettings, LegendCardSettings, BaseFontControlSettings, FontDefaultOptions} from "./TornadoChartSettingsModel"; import IVisualEventService = powerbi.extensibility.IVisualEventService; export class TornadoChart implements IVisual { private static ClassName: string = "tornado-chart"; private static Container: string = "tornadoContainer"; + private static Legend: ClassAndSelector = createClassAndSelector("legend"); + private static LegendItemSelector: ClassAndSelector = createClassAndSelector("legendItem"); private static Columns: ClassAndSelector = createClassAndSelector("columns"); private static Column: ClassAndSelector = createClassAndSelector("column"); private static Axes: ClassAndSelector = createClassAndSelector("axes"); @@ -249,10 +237,10 @@ export class TornadoChart implements IVisual { }; let highlight: number = NaN; - let highlightedValue: number = value; + let highlightedValue: number = 0; if (hasHighlights) { highlight = currentSeries.highlights[i]; - highlightedValue = highlight != null ? highlight : 0; + highlightedValue = (isNaN(highlight) || highlight === null || highlight === undefined) ? 0 : highlight; } dataPoints.push({ @@ -414,15 +402,16 @@ export class TornadoChart implements IVisual { private rootContainer: HTMLElement; private main: Selection; private columns: Selection; + private columnsSelection: Selection; private axes: Selection; private labels: Selection; private categories: Selection; - private clearCatcher: Selection; - private selectionManager: ISelectionManager; + private legendSelection: Selection; + private legendItems: Selection; + private gradients: Selection; private legend: ILegend; - private behavior: IInteractiveBehavior; - private interactivityService: IInteractivityServiceSelectable; + private behavior: TornadoWebBehavior; private hostService: IVisualHost; private localizationManager: ILocalizationManager; private isScrollVisible: boolean = false; @@ -461,14 +450,16 @@ export class TornadoChart implements IVisual { this.localizationManager = this.hostService.createLocalizationManager(); this.colors = options.host.colorPalette; this.colorHelper = new ColorHelper(this.colors); - this.selectionManager = options.host.createSelectionManager(); this.tooltipArgs = new TooltipArgsWrapper(options.element, options.host.tooltipService); - this.interactivityService = createInteractivitySelectionService(this.hostService); + this.legend = createLegend(options.element, false); + + const selectionManager = options.host.createSelectionManager(); + this.behavior = new TornadoWebBehavior(selectionManager, this.colorHelper); - const interactiveBehavior: IInteractiveBehavior = this.colorHelper.isHighContrast ? (new OpacityLegendBehavior()) : null; - this.legend = createLegend(options.element, false, this.interactivityService, true, null, interactiveBehavior); + this.formattingSettingsService = new FormattingSettingsService(this.localizationManager); + this.events = options.host.eventService; this.element = d3Select(options.element); this.rootContainer = document.createElement("div"); @@ -480,10 +471,11 @@ export class TornadoChart implements IVisual { .classed(TornadoChart.ClassName, true); const main: Selection = this.main = root.append("g"); - this.clearCatcher = appendClearCatcher(main); this.columns = main .append("g") - .classed(TornadoChart.Columns.className, true); + .classed(TornadoChart.Columns.className, true) + .attr("role", "listbox") + .attr("aria-multiselectable", "true"); this.axes = main .append("g") @@ -496,10 +488,9 @@ export class TornadoChart implements IVisual { this.categories = main .append("g") .classed(TornadoChart.Categories.className, true); - - this.behavior = new TornadoWebBehavior(); - this.formattingSettingsService = new FormattingSettingsService(this.localizationManager); - this.events = options.host.eventService; + + this.legendSelection = this.element + .select(TornadoChart.Legend.selectorName); } public update(options: VisualUpdateOptions): void { @@ -621,6 +612,20 @@ export class TornadoChart implements IVisual { private render(): void { this.renderLegend(); this.renderWithScrolling(); + this.bindBehaviorToVisual(); + } + + private bindBehaviorToVisual(): void { + const behaviorOptions: TornadoBehaviorOptions = { + columns: this.columnsSelection, + clearCatcher: this.root, + tooltipArgs: this.tooltipArgs, + legend: this.legendItems, + legendClearCatcher: this.legendSelection, + gradients: this.gradients + }; + this.behavior.bindEvents(behaviorOptions); + this.behavior.renderSelection(); } private clearData(): void { @@ -632,21 +637,11 @@ export class TornadoChart implements IVisual { this.legend.drawLegend({ dataPoints: [] }, this.viewport); } - public onClearSelection(): void { - if (this.interactivityService) { - this.interactivityService.clearSelection(); - } - } - private renderWithScrolling(): void { if (!this.dataView || !this.formattingSettings) { return; } - if (!this.dataView.hasHighlights) { - this.interactivityService.applySelectionStateToData(this.dataView.dataPoints); - } - this.computeHeightColumn(); this.renderMiddleSection(); this.renderAxes(); @@ -744,8 +739,6 @@ export class TornadoChart implements IVisual { } private renderColumns(columnsData: TornadoChartPoint[]): void { - const hasSelection: boolean = this.interactivityService && this.interactivityService.hasSelection(); - const columnsSelection: Selection = this.columns .selectAll(TornadoChart.Column.selectorName) .data(columnsData); @@ -754,7 +747,7 @@ export class TornadoChart implements IVisual { // otherwise gradients are duplicated this.columns.select("defs").remove(); - const gradients = this.columns.append("defs") + this.gradients = this.columns.append("defs") .selectAll("linearGradient") .data(columnsData) .enter() @@ -765,21 +758,6 @@ export class TornadoChart implements IVisual { .attr("x2", "100%") .attr("y2", "0%"); - // from left to right - // bright color - gradients.append("stop") - .attr("offset", (p: TornadoChartPoint) => (hasSelection && p.selected ? 100 : p.highlightedValue / p.value * 100) + "%") - .attr("stop-color", (p: TornadoChartPoint) => this.colorHelper.isHighContrast ? this.colorHelper.getThemeColor() : p.color) - .attr("stop-opacity", 1); - - // from right to left - // less bright color - // but % starts from left to right (so f.e 30% means end point will be at 30% starting from left, but coloring will start from right until reach end point) - gradients.append("stop") - .attr("offset", (p: TornadoChartPoint) => p.highlightedValue / p.value * 100 + "%") - .attr("stop-color", (p: TornadoChartPoint) => this.colorHelper.isHighContrast ? this.colorHelper.getThemeColor() : p.color) - .attr("stop-opacity", 0.5); - const columnsSelectionMerged = columnsSelection .enter() .append("svg:rect") @@ -787,58 +765,23 @@ export class TornadoChart implements IVisual { columnsSelectionMerged.classed(TornadoChart.Column.className, true); - // There should be better way to do this - // without it, when element selected + scrolled, bug appears (selected element is different) - if(!hasSelection) - { - columnsSelectionMerged + columnsSelectionMerged .style("stroke", (p: TornadoChartPoint) => p.color) .style("fill", (p: TornadoChartPoint) => "url(#gradient-" + p.uniqId + ")") .attr("transform", (p: TornadoChartPoint) => translateAndRotate(p.dx, p.dy, p.px, p.py, p.angle)) .attr("height", (p: TornadoChartPoint) => p.height) .attr("width", (p: TornadoChartPoint) => p.width) - .attr("tabindex", 0); - } - else - { - columnsSelectionMerged - .style("fill", (p: TornadoChartPoint) => this.colorHelper.isHighContrast ? this.colorHelper.getThemeColor() : p.color) - .style("stroke", (p: TornadoChartPoint) => p.color) - .style("fill-opacity", (p: TornadoChartPoint) => TornadoChartUtils.getOpacity( - p.selected, - p.highlight, - hasSelection, - this.dataView.hasHighlights)) - .style("stroke-opacity", (p: TornadoChartPoint) => TornadoChartUtils.getOpacity( - p.selected, - p.highlight, - hasSelection, - this.dataView.hasHighlights)) - .attr("transform", (p: TornadoChartPoint) => translateAndRotate(p.dx, p.dy, p.px, p.py, p.angle)) - .attr("height", (p: TornadoChartPoint) => p.height) - .attr("width", (p: TornadoChartPoint) => p.width) - .attr("tabindex", 0); - } + .attr("tabindex", 0) + .attr("role", "option") + .attr("aria-label", (d: TornadoChartPoint) => { + return `${d.tooltipData?.[0].displayName} = ${d.tooltipData?.[0].value}`; + }); columnsSelection .exit() .remove(); - const interactivityService = this.interactivityService; - - if (interactivityService) { - interactivityService.applySelectionStateToData(columnsData); - - const behaviorOptions: TornadoBehaviorOptions = { - columns: columnsSelectionMerged, - clearCatcher: this.clearCatcher, - interactivityService: this.interactivityService, - behavior: this.behavior, - dataPoints: columnsData, - tooltipArgs: this.tooltipArgs - }; - interactivityService.bind(behaviorOptions); - } + this.columnsSelection = columnsSelectionMerged; } private getColumnWidth(value: number, minValue: number, maxValue: number, width: number): number { @@ -1002,7 +945,8 @@ export class TornadoChart implements IVisual { .attr("font-weight", labelFontIsBold ? "bold" : "normal") .attr("font-style", labelFontIsItalic ? "italic" : "normal") .attr("text-decoration", labelFontIsUnderlined? "underline" : "normal") - .text((p: TornadoChartPoint) => p.label.value); + .text((p: TornadoChartPoint) => p.label.value) + .attr("role", "presentation"); labelSelection .exit() @@ -1113,7 +1057,9 @@ export class TornadoChart implements IVisual { } this.legend.drawLegend(legendData, { ...this.viewport }); - this.element.selectAll("g#legendGroup text") + this.legendItems = this.legendSelection.selectAll(TornadoChart.LegendItemSelector.selectorName); + + this.legendSelection.selectAll("text") .style("font-weight", () => this.formattingSettings.legendCardSettings.font.bold.value ? "bold" : "normal") .style("font-style", () => this.formattingSettings.legendCardSettings.font.italic.value ? "italic" : "normal") .style("text-decoration", () => this.formattingSettings.legendCardSettings.font.underline.value ? "underline" : "none"); diff --git a/src/TornadoWebBehavior.ts b/src/TornadoWebBehavior.ts index 1218668..4f02bf8 100644 --- a/src/TornadoWebBehavior.ts +++ b/src/TornadoWebBehavior.ts @@ -28,95 +28,189 @@ import { Selection as d3Selection } from "d3-selection"; type Selection = d3Selection; - -import { - interactivityBaseService -} from "powerbi-visuals-utils-interactivityutils"; -import ISelectionHandler = interactivityBaseService.ISelectionHandler; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; -import IInteractivityService = interactivityBaseService.IInteractivityService; - +import ISelectionManager = powerbi.extensibility.ISelectionManager; +import ISelectionId = powerbi.visuals.ISelectionId; import { TornadoBehaviorOptions, TornadoChartPoint } from "./interfaces"; import { TornadoChartUtils } from "./tornadoChartUtils"; import { createTooltipServiceWrapper, ITooltipServiceWrapper } from "powerbi-visuals-utils-tooltiputils"; +import { ColorHelper } from "powerbi-visuals-utils-colorutils"; +import { LegendDataPoint } from "powerbi-visuals-utils-chartutils/lib/legend/legendInterfaces"; -export class TornadoWebBehavior implements IInteractiveBehavior { - private columns: Selection; +export class TornadoWebBehavior { + private legendItems: Selection; + private columns: Selection; private clearCatcher: Selection; - private interactivityService: IInteractivityService; + private legendClearCatcher: Selection; + private dataPoints: TornadoChartPoint[]; + private legendDataPoints: LegendDataPoint[]; + private legendIcons: Selection; + private gradients: Selection; + private selectionManager: ISelectionManager; private tooltipServiceWrapper: ITooltipServiceWrapper; + private colorHelper: ColorHelper; - public bindEvents(options: TornadoBehaviorOptions, selectionHandler: ISelectionHandler) { - this.columns = options.columns; - this.clearCatcher = options.clearCatcher; - this.interactivityService = options.interactivityService; - this.tooltipServiceWrapper = createTooltipServiceWrapper( - options.tooltipArgs.tooltipService, - options.tooltipArgs.tooltipElement); - - this.tooltipServiceWrapper.addTooltip( - this.columns, - (tooltipEvent: TornadoChartPoint) => { - return tooltipEvent.tooltipData; - }, - (tooltipEvent: TornadoChartPoint) => { - return tooltipEvent.identity;} - ); + constructor(selectionManager: ISelectionManager, colorHelper: ColorHelper){ + this.selectionManager = selectionManager; + this.colorHelper = colorHelper; + this.selectionManager.registerOnSelectCallback(this.onSelectCallback.bind(this)); + } - this.columns.on("click", (event : PointerEvent, dataPoint: TornadoChartPoint) => { - event && selectionHandler.handleSelection( - dataPoint, - event.ctrlKey || event.metaKey || event.shiftKey); - }); + private onSelectCallback(selectionIds?: ISelectionId[]){ + this.applySelectionStateToData(selectionIds); + this.renderSelection(); + } - this.columns.on("keydown", (event : KeyboardEvent, dataPoint: TornadoChartPoint) => { - if(event?.code == "Enter" || event?.code == "Space") - { - selectionHandler.handleSelection( - dataPoint, - event.ctrlKey || event.metaKey || event.shiftKey); - } + private applySelectionStateToData(selectionIds?: ISelectionId[]): void{ + const selectedIds: ISelectionId[] = this.selectionManager.getSelectionIds(); + this.setSelectedToDataPoints(this.dataPoints, selectionIds || selectedIds); + this.setSelectedToDataPoints(this.legendDataPoints, selectionIds || selectedIds); + } + + private setSelectedToDataPoints(dataPoints: LegendDataPoint[] | TornadoChartPoint[], ids: ISelectionId[]): void{ + dataPoints.forEach((dataPoint: LegendDataPoint| TornadoChartPoint) => { + dataPoint.selected = false; + ids.forEach((selectedId: ISelectionId) => { + if (selectedId.includes(dataPoint.identity)) { + dataPoint.selected = true; + } + }); }); + } - //Handle contextmenu on columns - this.columns.on("contextmenu", (event: PointerEvent, dataPoint: TornadoChartPoint) => { - selectionHandler.handleContextMenu(dataPoint, + private bindContextMenuEvent(elements: Selection): void { + elements.on("contextmenu", (event: PointerEvent, dataPoint: TornadoChartPoint | LegendDataPoint | undefined) => { + this.selectionManager.showContextMenu(dataPoint ? dataPoint.identity : {}, { x: event.clientX, y: event.clientY } ); - event.preventDefault(); + event.preventDefault(); + event.stopPropagation(); }); + } - //Handle contextmenu on empty area - this.clearCatcher.on("contextmenu", (event: PointerEvent) => { - selectionHandler.handleContextMenu({"selected" : false}, - { - x: event.clientX, - y: event.clientY - }); - event.preventDefault(); - }); + private bindClickEvent(elements: Selection): void { + elements.on("click", (event: PointerEvent, dataPoint: TornadoChartPoint | LegendDataPoint | undefined) => { + const isMultiSelection: boolean = event.ctrlKey || event.metaKey || event.shiftKey; + if (dataPoint){ + this.selectionManager.select(dataPoint.identity, isMultiSelection); + event.stopPropagation(); + } + else { + this.selectionManager.clear(); + } + this.onSelectCallback(); + }) + } + + private bindKeyboardEvent(elements: Selection): void { + elements.on("keydown", (event : KeyboardEvent, dataPoint: TornadoChartPoint | LegendDataPoint) => { + if (event.code !== "Enter" && event.code !== "Space") { + return; + } + + const isMultiSelection: boolean = event.ctrlKey || event.metaKey || event.shiftKey; + this.selectionManager.select(dataPoint.identity, isMultiSelection); - this.clearCatcher.on("click", () => { - selectionHandler.handleClearSelection(); + event.stopPropagation(); + this.onSelectCallback(); }); } - public renderSelection(hasSelection: boolean) { - const hasHighlights: boolean = this.interactivityService.hasSelection(); - this.changeOpacityAttribute("fill-opacity", hasSelection, hasHighlights); - this.changeOpacityAttribute("stroke-opacity", hasSelection, hasHighlights); + public renderSelection(){ + const legendHasSelection: boolean = this.legendDataPoints.some((dataPoint: LegendDataPoint) => dataPoint.selected); + const dataPointHasSelection: boolean = this.dataPoints.some((dataPoint: TornadoChartPoint) => dataPoint.selected); + const dataPointHasHighlight: boolean = this.dataPoints.some((dataPoint: TornadoChartPoint) => dataPoint.highlight); + + this.legendIcons.style("fill-opacity", (legendDataPoint: LegendDataPoint) => { + return TornadoChartUtils.getLegendFillOpacity( + legendDataPoint.selected, + legendHasSelection, + this.colorHelper.isHighContrast + ); + }); + + this.legendIcons.style("fill", (legendDataPoint: LegendDataPoint) => { + return TornadoChartUtils.getLegendFill( + legendDataPoint.selected, + legendHasSelection, + legendDataPoint.color, + this.colorHelper.isHighContrast + ); + }); + + this.columns.attr("aria-selected", (dataPoint: TornadoChartPoint) => { + return dataPoint.selected + }); + this.applySelectionStyleAttribute(this.columns, "fill-opacity", dataPointHasSelection); + this.applySelectionStyleAttribute(this.columns, "stroke-opacity", dataPointHasSelection); + this.applyGradientsForHighlight(dataPointHasSelection, dataPointHasHighlight); } - private changeOpacityAttribute(attributeName: string, hasSelection: boolean, hasHighlights: boolean) { - this.columns.style(attributeName, (d: TornadoChartPoint) => { + private applyGradientsForHighlight(hasSelection: boolean, hasHighlight: boolean) { + this.gradients.selectAll("stop").remove(); + // from left to right + // bright color + this.gradients.append("stop") + .attr("offset", (p: TornadoChartPoint) => ((hasSelection && p.selected) || (!hasSelection && !hasHighlight) ? 100 : p.highlightedValue / p.value * 100) + "%") + .attr("stop-color", (p: TornadoChartPoint) => this.colorHelper.isHighContrast ? this.colorHelper.getThemeColor() : p.color) + .attr("stop-opacity", 1); + + // from right to left + // less bright color + // but % starts from left to right (so f.e 30% means end point will be at 30% starting from left, but coloring will start from right until reach end point) + this.gradients.append("stop") + .attr("offset", (p: TornadoChartPoint) => p.highlightedValue / p.value * 100 + "%") + .attr("stop-color", (p: TornadoChartPoint) => this.colorHelper.isHighContrast ? this.colorHelper.getThemeColor() : p.color) + .attr("stop-opacity", 0.4); + } + + private applySelectionStyleAttribute(elements: Selection, attributeName: string, hasSelection: boolean) { + elements.style(attributeName, (dataPoint: TornadoChartPoint) => { return TornadoChartUtils.getOpacity( - d.selected, - d.highlight, - !d.highlight && hasSelection, - !d.selected && hasHighlights); + dataPoint.selected, + dataPoint.highlight, + hasSelection, + this.colorHelper.isHighContrast); }); } + + public bindEvents(options: TornadoBehaviorOptions) { + this.columns = options.columns; + this.legendItems = options.legend; + this.dataPoints = options.columns.data(); + this.legendDataPoints = options.legend.data(); + this.clearCatcher = options.clearCatcher; + this.legendClearCatcher = options.legendClearCatcher; + this.legendIcons = options.legend.selectAll(".legendIcon"); + this.gradients = options.gradients; + + this.applySelectionStateToData(); + + this.tooltipServiceWrapper = createTooltipServiceWrapper( + options.tooltipArgs.tooltipService, + options.tooltipArgs.tooltipElement); + + this.tooltipServiceWrapper.addTooltip( + this.columns, + (tooltipEvent: TornadoChartPoint) => { + return tooltipEvent.tooltipData; + }, + (tooltipEvent: TornadoChartPoint) => { + return tooltipEvent.identity;} + ); + + this.bindContextMenuEvent(this.columns); + this.bindContextMenuEvent(this.legendItems); + this.bindContextMenuEvent(this.clearCatcher); + this.bindContextMenuEvent(this.legendClearCatcher); + + this.bindClickEvent(this.columns); + this.bindClickEvent(this.legendItems); + this.bindClickEvent(this.clearCatcher); + this.bindClickEvent(this.legendClearCatcher); + + this.bindKeyboardEvent(this.columns); + } } diff --git a/src/interfaces.ts b/src/interfaces.ts index d5b2aa9..93158e4 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -42,17 +42,11 @@ import { valueFormatter as vf} from "powerbi-visuals-utils-formattingutils"; import { TextProperties } from "powerbi-visuals-utils-formattingutils/lib/src/interfaces"; import IValueFormatter = vf.IValueFormatter; -import { - interactivitySelectionService as interactivityService, - interactivityBaseService -} from "powerbi-visuals-utils-interactivityutils"; -import SelectableDataPoint = interactivityService.SelectableDataPoint; -import IInteractivityService = interactivityBaseService.IInteractivityService; - import { legendInterfaces } from "powerbi-visuals-utils-chartutils"; import LegendData = legendInterfaces.LegendData; import ITooltipService = powerbiVisualsApi.extensibility.ITooltipService; +import { LegendDataPoint } from "powerbi-visuals-utils-chartutils/lib/legend/legendInterfaces"; export interface TornadoChartTextOptions { fontFamily?: string; @@ -84,7 +78,7 @@ export interface TornadoChartDataView { labelFormatter: TornadoChartLabelFormatter; } -export interface TornadoChartPoint extends SelectableDataPoint { +export interface TornadoChartPoint { uniqId: number; dx?: number; dy?: number; @@ -103,6 +97,8 @@ export interface TornadoChartPoint extends SelectableDataPoint { minValue: number; maxValue: number; formatString: string; + selected: boolean; + identity: ISelectionId; } export interface LabelData { @@ -126,11 +122,13 @@ export interface TextData { textProperties: TextProperties; } -export interface TornadoBehaviorOptions extends interactivityBaseService.IBehaviorOptions { - columns: Selection; +export interface TornadoBehaviorOptions { + columns: Selection; + legend: Selection; clearCatcher: Selection; - interactivityService: IInteractivityService; + legendClearCatcher: Selection; tooltipArgs: TooltipArgsWrapper; + gradients: Selection; } export interface TooltipCategoryDataItem { diff --git a/src/tornadoChartUtils.ts b/src/tornadoChartUtils.ts index 1c86de6..6ea6e2b 100644 --- a/src/tornadoChartUtils.ts +++ b/src/tornadoChartUtils.ts @@ -27,11 +27,37 @@ export class TornadoChartUtils { static DimmedOpacity: number = 0.4; static DefaultOpacity: number = 1.0; + static DimmedColor: string = "#A6A6A6"; - static getOpacity(selected: boolean, highlight: boolean, hasSelection: boolean, hasPartialHighlights: boolean): number { - if ((hasPartialHighlights && !highlight) || (hasSelection && !selected)) { + static getOpacity(selected: boolean, highlight: boolean, hasSelection: boolean, isHCM: boolean): number { + if (!highlight && hasSelection && !selected && isHCM) { return TornadoChartUtils.DimmedOpacity; } return TornadoChartUtils.DefaultOpacity; } + + static getLegendFillOpacity( + selected: boolean, + hasSelection: boolean, + isHCM: boolean): number { + + if ((hasSelection && !selected) && isHCM) { + return TornadoChartUtils.DimmedOpacity; + } + + return TornadoChartUtils.DefaultOpacity; + } + + static getLegendFill( + selected: boolean, + hasSelection: boolean, + defaultColor: string, + isHCM: boolean): string { + + if ((hasSelection && !selected) && !isHCM) { + return TornadoChartUtils.DimmedColor; + } + + return defaultColor; + } } \ No newline at end of file diff --git a/style/tornadoChart.less b/style/tornadoChart.less index c3d2d69..e56d6b9 100644 --- a/style/tornadoChart.less +++ b/style/tornadoChart.less @@ -28,7 +28,6 @@ * Imports external styles. * We compile it as a less file in order to wrap the external CSS rules. */ -@import (less) "/node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css"; @import (less) "/node_modules/powerbi-visuals-utils-chartutils/lib/index.css"; @regularFontFamily: helvetica, arial, sans-serif; diff --git a/test/TornadoChartBuilder.ts b/test/TornadoChartBuilder.ts index bc32546..11167ed 100644 --- a/test/TornadoChartBuilder.ts +++ b/test/TornadoChartBuilder.ts @@ -75,13 +75,21 @@ export class TornadoChartBuilder extends VisualBuilderBase { public get axis(): NodeListOf { return this.scrollable[0].querySelectorAll("g.axes > line.axis"); } - + + public get column(): HTMLElement { + return this.scrollable[0].querySelector("g.columns"); + } + public get columns(): NodeListOf { return this.scrollable[0].querySelectorAll("g.columns rect.column"); } - public get columnsDefs(): NodeListOf { - return this.scrollable[0].querySelectorAll("g.columns defs"); + public get columnsDefs(): HTMLElement { + return this.scrollable[0].querySelector("g.columns defs"); + } + + public get gradients(): NodeListOf { + return this.columnsDefs.querySelectorAll("linearGradient"); } public get labels(): NodeListOf { @@ -92,6 +100,15 @@ export class TornadoChartBuilder extends VisualBuilderBase { return this.labels[0].querySelectorAll("text.label-text"); } + public get selectedColumns(): Element[] { + return Array.from(this.gradients).filter((element: SVGElement) => { + const stopElement: SVGElement = element.querySelector("stop"); + const appliedOffset: string = stopElement.getAttribute("offset"); + + return appliedOffset === "100%"; + }); + } + public parseSeries( dataView: DataView, dataViewValueColumns: DataViewValueColumns, diff --git a/test/visualTest.ts b/test/visualTest.ts index 7ba8c76..6d03ab6 100644 --- a/test/visualTest.ts +++ b/test/visualTest.ts @@ -33,7 +33,7 @@ import DataViewValueColumnGroup = powerbiVisualsApi.DataViewValueColumnGroup; import ISelectionId = powerbiVisualsApi.visuals.ISelectionId; -import { assertColorsMatch } from "powerbi-visuals-utils-testutils"; +import { ClickEventType, assertColorsMatch, d3Click, renderTimeout } from "powerbi-visuals-utils-testutils"; import { TornadoData } from "./TornadoData"; import { TornadoChartBuilder } from "./TornadoChartBuilder"; @@ -437,16 +437,15 @@ describe("TornadoChart", () => { describe("Highligh test", () => { const expectedHighligtedCount: number = 1; let columns: HTMLElement[]; - let columnsDefs: HTMLElement[]; + let columnsDefs: HTMLElement; let dataViewWithHighLighted: DataView; beforeEach(() => { dataViewWithHighLighted = dataViewBuilder.getDataView(undefined, true); visualBuilder.update(dataViewWithHighLighted); - visualBuilder.updateRenderTimeout(dataViewWithHighLighted, () => { columns = Array.from(visualBuilder.columns); - columnsDefs = Array.from(visualBuilder.columnsDefs); + columnsDefs = visualBuilder.columnsDefs; }); }); @@ -454,8 +453,7 @@ describe("TornadoChart", () => { visualBuilder.updateRenderTimeout(dataViewWithHighLighted, () => { let highligtedCount: number = 0; let nonHighlightedCount: number = 0; - - Array.from(columnsDefs[0].children).forEach((element) => { + Array.from(columnsDefs.children).forEach((element) => { Array.from(element.children).forEach((childElement) => { if(childElement.outerHTML.indexOf("100%") != -1){ highligtedCount += 1; @@ -507,6 +505,182 @@ describe("TornadoChart", () => { }); }); }); + + describe("Selection tests", () => { + it("column can be selected", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + const firstColumn: HTMLElement = visualBuilder.columns[0]; + d3Click(firstColumn, 0, 0, ClickEventType.Default); + + renderTimeout(() => { + expect(visualBuilder.selectedColumns?.length).toBe(1); + done(); + }); + }); + }); + + it("column can be deselected", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + const firstColumn: HTMLElement = visualBuilder.columns[0]; + d3Click(firstColumn, 0, 0, ClickEventType.Default); + + renderTimeout(() => { + expect(visualBuilder.selectedColumns?.length).toBe(1); + d3Click(firstColumn, 0, 0, ClickEventType.CtrlKey); + + renderTimeout(() => { + expect(visualBuilder.selectedColumns?.length).toBe(12); + done(); + }); + }); + }); + }); + + it("multi-selection should work with ctrlKey", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + checkMultiselection(ClickEventType.CtrlKey, done); + }); + }); + + it("multi-selection should work with metaKey", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + checkMultiselection(ClickEventType.MetaKey, done); + }); + }); + + it("multi-selection should work with shiftKey", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + checkMultiselection(ClickEventType.ShiftKey, done); + }); + }); + + function checkMultiselection(eventType: number, done: DoneFn): void { + const firstColumn: HTMLElement = visualBuilder.columns[0]; + const secondColumn: HTMLElement = visualBuilder.columns[1]; + d3Click(firstColumn, 0, 0, ClickEventType.Default); + renderTimeout(() => { + expect(visualBuilder.selectedColumns?.length).toBe(1); + + d3Click(secondColumn, 0, 0, eventType); + + renderTimeout(() => { + expect(visualBuilder.selectedColumns?.length).toBe(2); + done(); + }); + }); + } + }); + + describe("Keyboard navigation and related aria-attributes tests:", () => { + it("should have role=listbox and aria-multiselectable attributes correctly set", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + const columnsElement: HTMLElement = visualBuilder.column; + + expect(columnsElement.getAttribute("role")).toBe("listbox"); + expect(columnsElement.getAttribute("aria-multiselectable")).toBe("true"); + + done(); + }); + }); + + it("should have role=presentation correctly set on text labels", (done) => { + visualBuilder.updateRenderTimeout(dataView, () => { + + const labels: SVGElement[] = Array.from(visualBuilder.labels).map((element: HTMLElement) => element.querySelector("text")); + for (const label of labels) { + expect(label.getAttribute("role")).toBe("presentation"); + } + + done(); + }); + }); + + it("enter toggles the correct column", () => { + const enterEvent = new KeyboardEvent("keydown", { code: "Enter", bubbles: true }); + checkKeyboardSingleSelection(enterEvent); + }); + + it("space toggles the correct column", () => { + const spaceEvent = new KeyboardEvent("keydown", { code: "Space", bubbles: true }); + checkKeyboardSingleSelection(spaceEvent); + }); + + it("multiselection should work with ctrlKey", () => { + const enterEventCtrlKey = new KeyboardEvent("keydown", { code: "Enter", bubbles: true, ctrlKey: true }); + checkKeyboardMultiSelection(enterEventCtrlKey); + }); + + it("multiselection should work with metaKey", () => { + const enterEventMetaKey = new KeyboardEvent("keydown", { code: "Enter", bubbles: true, metaKey: true }); + checkKeyboardMultiSelection(enterEventMetaKey); + }); + + it("multiselection should work with shiftKey", () => { + const enterEventShiftKey = new KeyboardEvent("keydown", { code: "Enter", bubbles: true, shiftKey: true }); + checkKeyboardMultiSelection(enterEventShiftKey); + }); + + it("column can be focused", () => { + visualBuilder.updateFlushAllD3Transitions(dataView); + + const columns: HTMLElement[] = Array.from(visualBuilder.columns); + const firstColumn: HTMLElement = columns[0]; + + columns.forEach((column: HTMLElement) => { + expect(column.matches(":focus-visible")).toBeFalse(); + }); + + firstColumn.focus(); + expect(firstColumn.matches(':focus-visible')).toBeTrue(); + + const otherColumns: HTMLElement[] = columns.slice(1); + otherColumns.forEach((column: HTMLElement) => { + expect(column.matches(":focus-visible")).toBeFalse(); + }); + + }); + + function checkKeyboardSingleSelection(keyboardSingleSelectionEvent: KeyboardEvent): void { + visualBuilder.updateFlushAllD3Transitions(dataView); + const columns: HTMLElement[] = Array.from(visualBuilder.columns); + const firstColumn: HTMLElement = columns[0]; + const secondColumn: HTMLElement = columns[1]; + + firstColumn.dispatchEvent(keyboardSingleSelectionEvent); + expect(firstColumn.getAttribute("aria-selected")).toBe("true"); + + const otherColumns: HTMLElement[] = columns.slice(1); + otherColumns.forEach((column: HTMLElement) => { + expect(column.getAttribute("aria-selected")).toBe("false"); + }); + + secondColumn.dispatchEvent(keyboardSingleSelectionEvent); + expect(secondColumn.getAttribute("aria-selected")).toBe("true"); + + columns.splice(1, 1); + columns.forEach((column: HTMLElement) => { + expect(column.getAttribute("aria-selected")).toBe("false"); + } + ); + } + + function checkKeyboardMultiSelection(keyboardMultiselectionEvent: KeyboardEvent): void { + visualBuilder.updateFlushAllD3Transitions(dataView); + const enterEvent = new KeyboardEvent("keydown", { code: "Enter", bubbles: true }); + const columns: HTMLElement[] = Array.from(visualBuilder.columns); + const firstColumn: HTMLElement = columns[0]; + const secondColumn: HTMLElement = columns[1]; + + // select first column + firstColumn.dispatchEvent(enterEvent); + // multiselect second column + secondColumn.dispatchEvent(keyboardMultiselectionEvent); + + expect(firstColumn.getAttribute("aria-selected")).toBe("true"); + expect(secondColumn.getAttribute("aria-selected")).toBe("true"); + expect(visualBuilder.selectedColumns?.length).toBe(2); + } + }); }); function delay(time) {