From a658e5a5fad60be8dd62472dcc20eb773199ae92 Mon Sep 17 00:00:00 2001 From: Iuliia Kulagina <86924383+kullJul@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:25:12 +0200 Subject: [PATCH] Remove interactivity utils (#127) * Remove behavior classes * Remove interactivity utils from legend * Remove interactivity utils from packages * Remove interactivity utils from labels * Fix legend tests * Increment version * Increase version to 7.0.0 * Add interface for selectable dataPoint * Fix eslint * Update docs * Remove interactive legend class * Update docs * Update changelog * Update changelog * Update docs * Use beta version --------- Co-authored-by: Iuliia Kulagina --- CHANGELOG.md | 5 + docs/api/legend.md | 7 - docs/dev/development-workflow.md | 4 +- docs/usage/installation-guide.md | 6 - docs/usage/usage-guide.md | 1 + package-lock.json | 37 +- package.json | 3 +- src/dataLabel/dataLabelUtils.ts | 8 +- src/index.ts | 6 - src/label/labelLayout.ts | 10 +- src/legend/behavior/legendBehavior.ts | 79 ----- src/legend/behavior/opacityLegendBehavior.ts | 61 ---- src/legend/interactiveLegend.ts | 277 --------------- src/legend/legend.ts | 17 +- src/legend/legendInterfaces.ts | 10 +- src/legend/svgLegend.ts | 45 +-- test/legendTest.ts | 353 +------------------ test/mocks/mockBehavior.ts | 118 ------- test/mocks/mockOpacityBehavior.ts | 51 --- 19 files changed, 31 insertions(+), 1067 deletions(-) delete mode 100644 src/legend/behavior/legendBehavior.ts delete mode 100644 src/legend/behavior/opacityLegendBehavior.ts delete mode 100644 src/legend/interactiveLegend.ts delete mode 100644 test/mocks/mockBehavior.ts delete mode 100644 test/mocks/mockOpacityBehavior.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cbe259..db00df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 7.0.0 +* Removed interactivityutils and related code +* Removed interactiveLegend class +* Changed createLegend function signature -> createLegend(HTMLElement, boolean, LegendPosition) + ## 6.0.4 * Updated powerbi-visuals-api to 5.9.0 and other utils diff --git a/docs/api/legend.md b/docs/api/legend.md index db5b00b..7012c3a 100644 --- a/docs/api/legend.md +++ b/docs/api/legend.md @@ -12,8 +12,6 @@ This helper function simplifies PowerBI Custom Visual legends creation. ```typescript function createLegend(legendParentElement: HTMLElement, // top visual element, container in which legend will be created - interactive: boolean, // indicates that legend should be interactive - interactivityService: IInteractivityService, // reference to IInteractivityService interface which need to create legend click events isScrollable: boolean = false, // indicates that legend could be scrollable or not legendPosition: LegendPosition = LegendPosition.Top // Position of the legend inside of legendParentElement container ): ILegend; @@ -31,13 +29,8 @@ function createLegend(legendParentElement: HTMLElement, // top visual e //... some other init calls - if (this.behavior) { - this.interactivityService = createInteractivityService(hostServices); - } this.legend = createLegend( element, - options.interactivity && options.interactivity.isInteractiveLegend, - this.interactivityService, true); } ``` diff --git a/docs/dev/development-workflow.md b/docs/dev/development-workflow.md index 5f669b3..6f09179 100644 --- a/docs/dev/development-workflow.md +++ b/docs/dev/development-workflow.md @@ -48,13 +48,13 @@ npm run build This command compiles less code to CSS and TypeScript code to JavaScript. The result of the compilation is available in the ```lib``` directory. ## How to lint the source code -We use [TSLint](https://github.com/palantir/tslint) as a linter for TypeScript code. To check source code you should run the following command: +We use [eslint](https://github.com/eslint/eslint) as a linter for TypeScript code. To check source code you should run the following command: ```bash npm run lint ``` -This command checks style of TypeScript code and provides a list of problems. Please address all of problems reported by TSLint before sending a pull request to the [repository](https://github.com/Microsoft/powerbi-visuals-utils-chartutils). +This command checks style of TypeScript code and provides a list of problems. Please address all of problems reported by eslint before sending a pull request to the [repository](https://github.com/Microsoft/powerbi-visuals-utils-chartutils). ## How to run unit tests locally We use [Jasmine](https://github.com/jasmine/jasmine) and [Karma](https://github.com/karma-runner/karma) to run unit tests. Please note, Karma requires Google Chrome to run unit tests. diff --git a/docs/usage/installation-guide.md b/docs/usage/installation-guide.md index 6df70c9..d03f0af 100644 --- a/docs/usage/installation-guide.md +++ b/docs/usage/installation-guide.md @@ -25,7 +25,6 @@ This command installs the package and adds a package as a dependency to your ``` The package contains ```d.ts``` declarations file, it's necessary for TypeScript compiler and it helps to develop your visuals fast and confident. You should add the following files to the ```files``` property of ```tsconfig.json```: * ```typings/index.d.ts``` * ```node_modules/powerbi-visuals-utils-formattingutils/lib/index.d.ts``` -* ```node_modules/powerbi-visuals-utils-interactivityutils/lib/index.d.ts``` * ```node_modules/powerbi-visuals-utils-svgutils/lib/index.d.ts``` * ```node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts``` * ```node_modules/powerbi-visuals-utils-chartutils/lib/index.d.ts``` @@ -37,7 +36,6 @@ As a result you will have the following file structure: "files": [ "typings/index.d.ts", "node_modules/powerbi-visuals-utils-formattingutils/lib/index.d.ts", - "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.d.ts", "node_modules/powerbi-visuals-utils-svgutils/lib/index.d.ts", "node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts", "node_modules/powerbi-visuals-utils-chartutils/lib/index.d.ts" @@ -53,7 +51,6 @@ To use the package with your custom visuals you should add the following files t * ```node_modules/powerbi-visuals-utils-typeutils/lib/index.js``` * ```node_modules/powerbi-visuals-utils-svgutils/lib/index.js``` * ```node_modules/powerbi-visuals-utils-formattingutils/lib/index.js``` -* ```node_modules/powerbi-visuals-utils-interactivityutils/lib/index.js``` * ```node_modules/powerbi-visuals-utils-chartutils/lib/index.js``` As a result you will have the following file structure: @@ -70,7 +67,6 @@ As a result you will have the following file structure: "node_modules/powerbi-visuals-utils-typeutils/lib/index.js", "node_modules/powerbi-visuals-utils-svgutils/lib/index.js", "node_modules/powerbi-visuals-utils-formattingutils/lib/index.js", - "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.js", "node_modules/powerbi-visuals-utils-chartutils/lib/index.js" ], "style": ..., @@ -81,12 +77,10 @@ As a result you will have the following file structure: ## Including CSS artifacts to the custom visual To use the package with your custom visuals you should import the following CSS files to your ```.less``` file: -* ```node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css``` * ```node_modules/powerbi-visuals-utils-chartutils/lib/index.css``` As a result you will have the following file structure: ```less -@import (less) "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css"; @import (less) "node_modules/powerbi-visuals-utils-chartutils/lib/index.css"; ``` diff --git a/docs/usage/usage-guide.md b/docs/usage/usage-guide.md index 18fd38b..abd6f94 100644 --- a/docs/usage/usage-guide.md +++ b/docs/usage/usage-guide.md @@ -6,3 +6,4 @@ This package contains the following classes, interfaces and methods: * [Axis Helper](../api/axis-helper.md) - provide all necessary methods to maintain chart axes * [DataLabelManager](../api/data-label-manager.md) - helps to create and maintain labels * [DataLabelUtils](../api/data-label-utils.md) - label manager utils +* [Legend](../api/legend.md) - helps to create and mantain legend diff --git a/package-lock.json b/package-lock.json index 16fbe4a..fb5f96d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "powerbi-visuals-utils-chartutils", - "version": "6.0.4", + "version": "7.0.0-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "powerbi-visuals-utils-chartutils", - "version": "6.0.4", + "version": "7.0.0-beta.1", "license": "MIT", "dependencies": { "d3-array": "^3.2.4", @@ -15,7 +15,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" }, @@ -5116,11 +5115,6 @@ "node": ">=16" } }, - "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==" - }, "node_modules/powerbi-visuals-api": { "version": "5.9.0", "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.9.0.tgz", @@ -5169,17 +5163,6 @@ "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==", - "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", @@ -10532,11 +10515,6 @@ "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", "dev": true }, - "powerbi-models": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/powerbi-models/-/powerbi-models-1.13.0.tgz", - "integrity": "sha512-fToQmRqECBJSlHaKNAzFql52ryNnhSm2UwRXfsctcS5Hp//o9sExasVsASv6jZjXE8ACNyKjDUKdGqWsCjRd1Q==" - }, "powerbi-visuals-api": { "version": "5.9.0", "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.9.0.tgz", @@ -10583,17 +10561,6 @@ "powerbi-visuals-utils-typeutils": "^6.0.3" } }, - "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==", - "requires": { - "d3-selection": "^3.0.0", - "powerbi-models": "1.13.0", - "powerbi-visuals-utils-svgutils": "^6.0.4", - "powerbi-visuals-utils-typeutils": "^6.0.3" - } - }, "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", diff --git a/package.json b/package.json index 7b35431..07325c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "powerbi-visuals-utils-chartutils", - "version": "6.0.4", + "version": "7.0.0-beta.1", "description": "ChartUtils", "main": "lib/index.js", "module": "lib/index.js", @@ -72,7 +72,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" }, diff --git a/src/dataLabel/dataLabelUtils.ts b/src/dataLabel/dataLabelUtils.ts index 57d50c1..ed89d72 100644 --- a/src/dataLabel/dataLabelUtils.ts +++ b/src/dataLabel/dataLabelUtils.ts @@ -49,10 +49,6 @@ import * as svg from "powerbi-visuals-utils-svgutils"; import ClassAndSelector = svg.CssConstants.ClassAndSelector; import createClassAndSelector = svg.CssConstants.createClassAndSelector; -// powerbi.extensibility.utils.interactivity -import { interactivitySelectionService } from "powerbi-visuals-utils-interactivityutils"; -import SelectionDataPoint = interactivitySelectionService.SelectableDataPoint; - import * as dataLabelInterfaces from "./dataLabelInterfaces"; import LabelFormattedTextOptions = dataLabelInterfaces.LabelFormattedTextOptions; import LabelEnabledDataPoint = dataLabelInterfaces.LabelEnabledDataPoint; @@ -216,7 +212,7 @@ export function drawDefaultLabelsForDataPointChart(data: any[], context: Selecti .transition("") .duration(animationDuration) // .style(layout.style as any) - .style("opacity", (hasSelection ? (d: SelectionDataPoint) => getFillOpacity(d.selected, false, hasSelection, false) : 1) as any) + .style("opacity", (hasSelection ? d => getFillOpacity(d.selected, false, hasSelection, false) : 1) as any) .attr( "x", (d: LabelEnabledDataPoint) => d.labelX ) @@ -260,7 +256,7 @@ function selectLabels(filteredData: LabelEnabledDataPoint[], context: Selection< const getIdentifier = hasKey ? (d: any) => d.key : hasDataPointIdentity ? - (d: SelectionDataPoint) => (d.identity as ISelectionId).getKey() + d => (d.identity as ISelectionId).getKey() : undefined; const labels: Selection = isDonut ? diff --git a/src/index.ts b/src/index.ts index c97d1a6..09a1a3e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,10 +9,7 @@ import DataLabelManager from "./dataLabel/dataLabelManager"; import * as dataLabelUtils from "./dataLabel/dataLabelUtils"; import * as locationConverter from "./dataLabel/locationConverter"; -import * as interactiveLegend from "./legend/interactiveLegend"; import * as legend from "./legend/legend"; -import * as legendBehavior from "./legend/behavior/legendBehavior"; -import OpacityLegendBehavior from "./legend/behavior/opacityLegendBehavior"; import * as legendData from "./legend/legendData"; import * as legendInterfaces from "./legend/legendInterfaces"; import * as legendPosition from "./legend/legendPosition"; @@ -30,11 +27,8 @@ export { DataLabelManager, dataLabelUtils, locationConverter, - interactiveLegend, label, legend, - legendBehavior, - OpacityLegendBehavior, legendData, legendInterfaces, legendPosition, diff --git a/src/label/labelLayout.ts b/src/label/labelLayout.ts index 6b7b7c9..954f525 100644 --- a/src/label/labelLayout.ts +++ b/src/label/labelLayout.ts @@ -32,10 +32,6 @@ import { IRect, } from "powerbi-visuals-utils-svgutils"; -import { - interactivitySelectionService -} from "powerbi-visuals-utils-interactivityutils"; - import { FontProperties, inherit } from "./fontProperties"; import * as LabelUtils from "./labelUtils"; import * as Units from "./units"; @@ -43,6 +39,8 @@ import * as Units from "./units"; import * as DataLabelRectPositioner from "./dataLabelRectPositioner"; import * as DataLabelPointPositioner from "./dataLabelPointPositioner"; +import { ISelectableDataPoint } from "../legend/legendInterfaces"; + export enum LabelOrientation { Vertical = 0, Horizontal = 1, @@ -324,7 +322,7 @@ export interface LabelDataPointGroup { labelOrientation?: LabelOrientation; } -export interface Label extends interactivitySelectionService.SelectableDataPoint { +export interface Label extends ISelectableDataPoint { /** Text to be displayed in the label */ text: string; @@ -363,7 +361,7 @@ export interface Label extends interactivitySelectionService.SelectableDataPoint backgroundTransparency?: number; } -export interface LabelOld extends interactivitySelectionService.SelectableDataPoint { +export interface LabelOld extends ISelectableDataPoint{ /** Text to be displayed in the label */ text: string; diff --git a/src/legend/behavior/legendBehavior.ts b/src/legend/behavior/legendBehavior.ts deleted file mode 100644 index 9309933..0000000 --- a/src/legend/behavior/legendBehavior.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* -* Power BI Visualizations -* -* Copyright (c) Microsoft Corporation -* All rights reserved. -* MIT License -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the ""Software""), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -import { Selection } from "d3-selection"; - -// powerbi.extensibility.utils.interactivity -import { LegendDataPoint } from "../legendInterfaces"; -import { interactivityBaseService, interactivityUtils } from "powerbi-visuals-utils-interactivityutils"; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; -import ISelectionHandler = interactivityBaseService.ISelectionHandler; -import IBehaviorOptions = interactivityBaseService.IBehaviorOptions; - -export interface LegendBehaviorOptions extends IBehaviorOptions { - legendItems: Selection; - legendIcons: Selection; - clearCatcher: Selection; -} - -export class LegendBehavior implements IInteractiveBehavior { - public static dimmedLegendColor = "#A6A6A6"; - protected legendIcons; - - public bindEvents(options: LegendBehaviorOptions, selectionHandler: ISelectionHandler): void { - const legendItems = options.legendItems; - this.legendIcons = options.legendIcons; - const clearCatcher = options.clearCatcher; - - interactivityUtils.registerStandardSelectionHandler(legendItems, selectionHandler); - - clearCatcher.on("click", () => { - selectionHandler.handleClearSelection(); - }); - } - - public renderSelection(hasSelection: boolean): void { - if (hasSelection) { - this.legendIcons.style( - "fill", (d: LegendDataPoint) => { - if (!d.selected) { - return LegendBehavior.dimmedLegendColor; - } - else { - return d.color; - } - } - ); - } - else { - this.legendIcons.style( - "fill", (d: LegendDataPoint) => { - return d.color; - } - ); - } - } -} diff --git a/src/legend/behavior/opacityLegendBehavior.ts b/src/legend/behavior/opacityLegendBehavior.ts deleted file mode 100644 index 39b56f6..0000000 --- a/src/legend/behavior/opacityLegendBehavior.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Power BI Visualizations - * - * Copyright (c) Microsoft Corporation - * All rights reserved. - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the ""Software""), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -import { LegendDataPoint } from "../legendInterfaces"; -import { LegendBehavior } from "./legendBehavior"; - -// Interactivity utils -import { interactivityBaseService } from "powerbi-visuals-utils-interactivityutils"; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; - -export default class OpacityLegendBehavior extends LegendBehavior implements IInteractiveBehavior { - public static dimmedOpacity: number = 0.4; - public static defaultOpacity: number = 1; - public renderSelection(hasSelection: boolean): void { - if (hasSelection) { - this.legendIcons.style( - "fill", (d: LegendDataPoint) => { - return d.color; - }) - .style( - "fill-opacity", (d: LegendDataPoint) => { - if (!d.selected) { - return OpacityLegendBehavior.dimmedOpacity; - } - else { - return OpacityLegendBehavior.defaultOpacity; - } - }); - } - else { - this.legendIcons.style( - "fill", (d: LegendDataPoint) => { - return d.color; - }) - .style("fill-opacity", OpacityLegendBehavior.defaultOpacity); - } - } -} diff --git a/src/legend/interactiveLegend.ts b/src/legend/interactiveLegend.ts deleted file mode 100644 index b45b846..0000000 --- a/src/legend/interactiveLegend.ts +++ /dev/null @@ -1,277 +0,0 @@ -/* -* Power BI Visualizations -* -* Copyright (c) Microsoft Corporation -* All rights reserved. -* MIT License -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the ""Software""), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -/* eslint-disable powerbi-visuals/no-implied-inner-html */ -import { select, Selection } from "d3-selection"; - -import powerbi from "powerbi-visuals-api"; -import { textUtil } from "powerbi-visuals-utils-formattingutils"; - -import { ILegend, LegendData, LegendDataPoint, LegendPosition } from "./legendInterfaces"; - -export class InteractiveLegend implements ILegend { - private static LegendHeight = 70; - private static LegendContainerClass = "interactive-legend"; - private static LegendContainerSelector = ".interactive-legend"; - private static LegendTitleClass = "title"; - private static LegendItem = "item"; - private static legendPlaceSelector = "\u25CF"; - private static legendIconClass = "icon"; - private static legendColorCss = "color"; - private static legendItemNameClass = "itemName"; - private static legendItemMeasureClass = "itemMeasure"; - - private legendContainerParent: Selection; - private legendContainerDiv: Selection; - - constructor(element: HTMLElement) { - this.legendContainerParent = select(element); - } - - public getMargins(): powerbi.IViewport { - return { - height: InteractiveLegend.LegendHeight, - width: 0 - }; - } - - public drawLegend(legendData: LegendData) { - const data = legendData.dataPoints; - if (data.length < 1) { - return; - } - - let legendContainerDiv = this.legendContainerParent.select(InteractiveLegend.LegendContainerSelector); - - if (legendContainerDiv.empty()) { - if (!data.length) { - return; - } - - legendContainerDiv = this.legendContainerParent - .insert("div", ":first-child") - .style("height", this.getMargins().height) - .classed(InteractiveLegend.LegendContainerClass, true); - } - - this.legendContainerDiv = legendContainerDiv; - - // Construct the legend title and items. - this.drawTitle(data); - this.drawLegendItems(data); - } - - public reset(): void { - if (this.legendContainerDiv) { - this.legendContainerDiv.remove(); - this.legendContainerDiv = null; - } - } - - public isVisible(): boolean { - return true; - } - - /** - * Not supported - */ - /* eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */ - public changeOrientation(orientation: LegendPosition) { } - - public getOrientation(): LegendPosition { - return LegendPosition.Top; - } - - /** - * Draw the legend title - */ - private drawTitle(data: LegendDataPoint[]): void { - const titleDiv: Selection = this.legendContainerDiv.selectAll(`div.${InteractiveLegend.LegendTitleClass}`), - item: Selection = titleDiv.data([data[0]]); - - // Enter - const itemEnter: Selection = item.enter(), - titleDivEnter: Selection = itemEnter - .append("div") - .attr("class", InteractiveLegend.LegendTitleClass); - - titleDivEnter - .filter((d: LegendDataPoint) => d.iconOnlyOnLabel) - .append("span") - .attr("class", InteractiveLegend.legendIconClass) - .html(InteractiveLegend.legendPlaceSelector); - - titleDivEnter.append("span"); - - // Update - item.filter((d: LegendDataPoint) => d.iconOnlyOnLabel) - .merge(itemEnter) - .select("span." + InteractiveLegend.legendIconClass) - .style(InteractiveLegend.legendColorCss, (d: LegendDataPoint) => d.color); - - item - .merge(itemEnter) - .select("span:last-child") - .text((d: LegendDataPoint) => d.category); - } - - /** - * Draw the legend items - */ - private drawLegendItems(data: LegendDataPoint[]): void { - // Add Mesaures - the items of the category in the legend - this.ensureLegendTableCreated(); - - const dataPointsMatrix: LegendDataPoint[][] = [data]; - const legendItemsContainer: Selection = this.legendContainerDiv - .select("tbody") - .selectAll("tr") - .data(dataPointsMatrix); - - // Enter - const legendItemsEnter: Selection = legendItemsContainer.enter(), - rowEnter: Selection = legendItemsEnter.append("tr"); - - const cellEnter: Selection = rowEnter - .selectAll("td") - .data((d: LegendDataPoint[]) => d, (d: LegendDataPoint) => d.label) - .enter() - .append("td") - .attr("class", InteractiveLegend.LegendItem); - - const cellSpanEnter: Selection = cellEnter.append("span"); - - cellSpanEnter.filter((d: LegendDataPoint) => !d.iconOnlyOnLabel) - .append("span") - .html(InteractiveLegend.legendPlaceSelector) - .attr("class", InteractiveLegend.legendIconClass) - .attr("white-space", "nowrap") - .style( - "font-size", "20px" // this creates a circle of 10px - ) - .style( - "margin-bottom", "7px" - ); - - cellSpanEnter - .append("span") - .attr("class", InteractiveLegend.legendItemNameClass); - - cellSpanEnter - .append("span") - .attr("class", InteractiveLegend.legendItemMeasureClass); - - // Update - const legendCells: Selection = legendItemsContainer - .merge(legendItemsEnter) - .selectAll("td") - .data((d: LegendDataPoint[]) => d, (d: LegendDataPoint) => d.label); - - legendCells - .merge(legendItemsEnter) - .select(`span.${InteractiveLegend.legendItemNameClass}`) - .html((d: LegendDataPoint) => textUtil.removeBreakingSpaces(d.label)); - - legendCells - .merge(legendItemsEnter) - .select(`span.${InteractiveLegend.legendItemMeasureClass}`) - .html((d: LegendDataPoint) => ` ${d.measure}`); - - legendCells - .merge(legendItemsEnter) - .select("span." + InteractiveLegend.legendIconClass) - .style("color", (d: LegendDataPoint) => d.color); - - // Exit - legendCells - .exit() - .remove(); - } - - /** - * Ensure legend table is created and set horizontal pan gestures on it - */ - private ensureLegendTableCreated(): void { - if (this.legendContainerDiv.select("div table").empty()) { - const legendTable: Selection = this.legendContainerDiv - .append("div") - .append("table"); - - legendTable.style("table-layout", "fixed").append("tbody"); - // Setup Pan Gestures of the legend - - // this.setPanGestureOnLegend(legendTable); - } - } - - /** - * Set Horizontal Pan gesture for the legend - */ - - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - private setPanGestureOnLegend(legendTable: Selection): void { - throw "Not implemented"; - // let parentNode = this.legendContainerParent.node(); - // let viewportWidth: number = parentNode.getBoundingClientRect().width; - // let xscale: d3.ScaleLinear = d3.scaleLinear() - // .domain([0, viewportWidth]) - // .range([0, viewportWidth]); - - // let legendZoom: d3.ZoomBehavior = d3.zoom() - // .scaleExtent([1, 1]) // disable scaling - // // .x(xscale) ? ? ? - // .on("zoom", () => { - // // horizontal pan is valid only in case the legend items width are bigger than the viewport width - // if ($(legendTable[0]).width() > viewportWidth) { - // let t: number[] = legendZoom; - // let tx: number = t[0]; - // let ty: number = t[1]; - - // tx = Math.min(tx, 0); - // tx = Math.max(tx, viewportWidth - $(legendTable[0]).width()); - // legendZoom.translate([tx, ty]); - - // legendTable.style("-ms-transform", () => { /* IE 9 */ - // return translateXWithPixels(tx); - // }); - - // legendTable.style("-webkit-transform", () => { /* Safari */ - // return translateXWithPixels(tx); - // }); - - // legendTable.style("transform", () => { - // return translateXWithPixels(tx); - // }); - // } - // }); - - // if (this.legendContainerDiv) { - // this.legendContainerDiv.call(legendZoom); - // } else { - // legendTable.call(legendZoom); - // } - } -} diff --git a/src/legend/legend.ts b/src/legend/legend.ts index f57dfe8..9197a7d 100644 --- a/src/legend/legend.ts +++ b/src/legend/legend.ts @@ -24,27 +24,16 @@ * THE SOFTWARE. */ import { Selection } from "d3-selection"; -import { interactivityBaseService } from "powerbi-visuals-utils-interactivityutils"; -import IInteractivityService = interactivityBaseService.IInteractivityService; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; -import { ILegend, LegendPosition, LegendDataPoint } from "./legendInterfaces"; -import { InteractiveLegend } from "./interactiveLegend"; +import { ILegend, LegendPosition } from "./legendInterfaces"; import { SVGLegend } from "./svgLegend"; export function createLegend( legendParentElement: HTMLElement, - interactive: boolean, - interactivityService: IInteractivityService, isScrollable: boolean = false, - legendPosition: LegendPosition = LegendPosition.Top, - interactiveBehavior?: IInteractiveBehavior, + legendPosition: LegendPosition = LegendPosition.Top ): ILegend { - if (interactive) { - return new InteractiveLegend(legendParentElement); - } - - return new SVGLegend(legendParentElement, legendPosition, interactivityService, isScrollable, interactiveBehavior); + return new SVGLegend(legendParentElement, legendPosition, isScrollable); } export function isLeft(orientation: LegendPosition): boolean { diff --git a/src/legend/legendInterfaces.ts b/src/legend/legendInterfaces.ts index e084a2a..65c48c2 100644 --- a/src/legend/legendInterfaces.ts +++ b/src/legend/legendInterfaces.ts @@ -26,9 +26,8 @@ import powerbi from "powerbi-visuals-api"; import { Point } from "powerbi-visuals-utils-svgutils"; -import { interactivitySelectionService } from "powerbi-visuals-utils-interactivityutils"; -import SelectableDataPoint = interactivitySelectionService.SelectableDataPoint; +import ISelectionId = powerbi.visuals.ISelectionId; export enum LegendPosition { Top, @@ -42,6 +41,11 @@ export enum LegendPosition { LeftCenter, } +export interface ISelectableDataPoint{ + selected: boolean, + identity: ISelectionId; +} + export interface LegendPosition2D { textPosition?: Point; glyphPosition?: Point; @@ -61,7 +65,7 @@ export enum LineStyle { dashdot = "dashdot", } -export interface LegendDataPoint extends SelectableDataPoint, LegendPosition2D { +export interface LegendDataPoint extends LegendPosition2D, ISelectableDataPoint { label: string; color: string; category?: string; diff --git a/src/legend/svgLegend.ts b/src/legend/svgLegend.ts index d3f48aa..23fdb18 100644 --- a/src/legend/svgLegend.ts +++ b/src/legend/svgLegend.ts @@ -39,12 +39,6 @@ import { } from "powerbi-visuals-utils-svgutils"; import { ILegend, LegendData, LegendDataPoint, LegendPosition } from "./legendInterfaces"; -import { LegendBehavior, LegendBehaviorOptions } from "./behavior/legendBehavior"; -import { - interactivityBaseService -} from "powerbi-visuals-utils-interactivityutils"; -import IInteractivityService = interactivityBaseService.IInteractivityService; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; import * as Markers from "./markers"; @@ -63,10 +57,6 @@ import TextProperties = interfaces.TextProperties; import ClassAndSelector = CssConstants.ClassAndSelector; import createClassAndSelector = CssConstants.createClassAndSelector; -// powerbi.extensibility.utils.interactivity -import appendClearCatcher = interactivityBaseService.appendClearCatcher; -import dataHasSelection = interactivityBaseService.dataHasSelection; - /* eslint-disable no-case-declarations */ export interface TitleLayout { @@ -110,10 +100,7 @@ export class SVGLegend implements ILegend { private parentViewport: powerbi.IViewport; private svg: Selection; private group: Selection; - private clearCatcher: Selection; private element: HTMLElement; - private interactivityService: IInteractivityService; - private interactiveBehavior?: IInteractiveBehavior; private legendDataStartIndex = 0; private arrowPosWindow = 1; private data: LegendData; @@ -150,9 +137,7 @@ export class SVGLegend implements ILegend { constructor( element: HTMLElement, legendPosition: LegendPosition, - interactivityService: IInteractivityService, - isScrollable: boolean, - interactiveBehavior?: IInteractiveBehavior + isScrollable: boolean ) { this.svg = select(element) @@ -162,16 +147,10 @@ export class SVGLegend implements ILegend { this.svg.style("display", "inherit"); this.svg.classed("legend", true); - if (interactivityService) { - this.clearCatcher = appendClearCatcher(this.svg); - } - this.group = this.svg .append("g") .attr("id", "legendGroup"); - this.interactiveBehavior = interactiveBehavior ? interactiveBehavior : new LegendBehavior(); - this.interactivityService = interactivityService; this.isScrollable = isScrollable; this.element = element; this.changeOrientation(legendPosition); @@ -273,9 +252,6 @@ export class SVGLegend implements ILegend { this.parentViewport = viewport; this.data = data; - if (this.interactivityService) - this.interactivityService.applySelectionStateToData(data.dataPoints); - if (data.dataPoints.length === 0) { this.changeOrientation(LegendPosition.None); } @@ -298,7 +274,6 @@ export class SVGLegend implements ILegend { const layout = this.calculateLayout(data, autoWidth); const titleLayout = layout.title; const titleData = titleLayout ? [titleLayout] : []; - const hasSelection = this.interactivityService && dataHasSelection(data.dataPoints); const group = this.group; @@ -369,7 +344,7 @@ export class SVGLegend implements ILegend { .append("title") .text((d: LegendDataPoint) => d.tooltip); - const mergedLegendIcons = legendItems + legendItems .merge(itemsEnter) .select(SVGLegend.LegendIcon.selectorName) .attr("transform", (dataPoint: LegendDataPoint) => { @@ -422,22 +397,6 @@ export class SVGLegend implements ILegend { .style("font-size", PixelConverter.fromPoint(data.fontSize)) .style("font-family", data.fontFamily); - if (this.interactivityService) { - const behaviorOptions: LegendBehaviorOptions = { - legendItems: mergedLegendItems, - legendIcons: mergedLegendIcons, - clearCatcher: this.clearCatcher, - dataPoints: data.dataPoints, - behavior: this.interactiveBehavior, - interactivityServiceOptions: { - isLegend: true - } - }; - - this.interactivityService.bind(behaviorOptions); - this.interactiveBehavior.renderSelection(hasSelection); - } - legendItems .exit() .remove(); diff --git a/test/legendTest.ts b/test/legendTest.ts index 6d4f4df..d47d46a 100644 --- a/test/legendTest.ts +++ b/test/legendTest.ts @@ -35,13 +35,6 @@ import IVisualHost = powerbi.extensibility.visual.IVisualHost; import { manipulation, Rect } from "powerbi-visuals-utils-svgutils"; import flushAllD3Transitions = manipulation.flushAllD3Transitions; -// powerbi.extensibility.utils.interactivity -import { interactivitySelectionService, interactivityBaseService } from "powerbi-visuals-utils-interactivityutils"; -import appendClearCatcher = interactivityBaseService.appendClearCatcher; -import IBehaviorOptions = interactivityBaseService.IBehaviorOptions; -import IInteractivityService = interactivityBaseService.IInteractivityService; -import createInteractivityService = interactivitySelectionService.createInteractivitySelectionService; - // powerbi.extensibility.utils.formatting import { stringExtensions } from "powerbi-visuals-utils-formattingutils"; @@ -64,11 +57,6 @@ import { LegendData, LegendPosition, legendProps, ILegend, LegendDataPoint } fro import * as legendPosition from "./../src/legend/legendPosition"; import { assertColorsMatch, findElementTitle } from "./helpers/helpers"; -import MockBehavior from "./mocks/mockBehavior"; - -import MockOpacityBehavior from "./mocks/mockOpacityBehavior"; -import { LegendBehaviorOptions } from "../src/legend/behavior/legendBehavior"; -import { SelectableDataPoint } from "powerbi-visuals-utils-interactivityutils/lib/interactivitySelectionService"; let incr: number = 0; @@ -87,7 +75,6 @@ describe("legend", () => { let element: HTMLElement, viewport: powerbi.IViewport, legend: ILegend, - interactivityService: IInteractivityService, hostServices: IVisualHost, legendData: LegendDataPoint[], legendTitleClassSelector = ".legendTitle"; @@ -96,8 +83,7 @@ describe("legend", () => { element = testDom("500", "500"); hostServices = createVisualHost({}); - interactivityService = createInteractivityService(hostServices); - legend = createLegend(element, false, interactivityService, true); + legend = createLegend(element, true); viewport = { height: parseFloat(element.getAttribute("height")), @@ -254,141 +240,6 @@ describe("legend", () => { }, DefaultWaitForRender); }); - describe("Legend interactivity tests", () => { - let icons: NodeListOf; - - beforeEach(() => { - legend.drawLegend({ dataPoints: legendData }, viewport); - icons = element.querySelectorAll(".legendIcon"); - }); - - it("Default state", () => { - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#0000ff"); - assertColorsMatch(icons[2].style.fill, "#00ff00"); - }); - - // click to clearCatcher fires, test doesn't work - xit("Click first legend", () => { - d3Click.call(icons[0], icons[0], 0, 0); - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#a6a6a6"); - }); - - xit("Click the last legend item, should just select current and clear others", () => { - d3Click.call(icons[0], icons[0], 0, 0); - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#a6a6a6"); - - d3Click.call(icons[icons.length - 1], icons[icons.length - 1], 0, 0); - assertColorsMatch(icons[0].style.fill, "#a6a6a6"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#00ff00"); - }); - - xit("Control + Click legend item, should multiselect", () => { - d3Click.call(icons[icons.length - 1], icons[icons.length - 1], 0, 0); - assertColorsMatch(icons[0].style.fill, "#a6a6a6"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#00ff00"); - - d3Click.call(icons[0], icons[0], 0, 0, ClickEventType.CtrlKey); - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#00ff00"); - }); - - xit("Click the clear catcher should clear the legend selection", () => { - d3Click.call(icons[0], icons[0], 0, 0); - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#a6a6a6"); - - d3Click.call(element.querySelector(".clearCatcher"), element.querySelector(".clearCatcher"), 0, 0); - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#0000ff"); - assertColorsMatch(icons[2].style.fill, "#00ff00"); - }); - - it("with opacity legend behavior", () => { - let mockDatapoints = [ - { label: "California", color: "#ff0000", identity: legendData[0].identity, selected: false }, - { label: "Texas", color: "#0000ff", identity: legendData[1].identity, selected: false }, - { label: "Washington", color: "#00ff00", identity: legendData[2].identity, selected: false } - ]; - - let behavior = new MockOpacityBehavior(); - const svg = select(element.querySelector("div#jasmine-fixtures svg")); - const clearCatcher = appendClearCatcher(svg); - const itemsSelection = svg.select("#legendGroup").selectAll(".legendItem"); - - let behaviorOptions: LegendBehaviorOptions = { - legendItems: itemsSelection, - legendIcons: itemsSelection, - clearCatcher: clearCatcher, - behavior: behavior, - dataPoints: mockDatapoints - }; - - interactivityService.bind(behaviorOptions); - behavior.uploadPoints(mockDatapoints); - behavior.selectIndex(1); - legend.drawLegend({ dataPoints: legendData }, viewport); - - itemsSelection.each((data, index, nodeList) => { - if (index === 1) { - expect((nodeList[index]).style.fillOpacity).toEqual("1"); - } else { - expect((nodeList[index]).style.fillOpacity).toEqual("0.4"); - } - }); - }); - - describe("with pre-existing selection state", () => { - beforeEach(() => { - let mockDatapoints = [ - { label: "California", color: "#ff0000", identity: legendData[0].identity, selected: false }, - { label: "Texas", color: "#0000ff", identity: legendData[1].identity, selected: false }, - { label: "Washington", color: "#00ff00", identity: legendData[2].identity, selected: false } - ]; - - let mockBehavior = new MockBehavior(mockDatapoints); - let behaviorOptions: IBehaviorOptions = { - behavior: mockBehavior, - dataPoints: mockDatapoints - }; - interactivityService.bind(behaviorOptions); - mockBehavior.selectIndex(1); - - legend.drawLegend({ dataPoints: legendData }, viewport); - }); - - it("has correct selection fill", () => { - assertColorsMatch(icons[0].style.fill, "#a6a6a6"); - assertColorsMatch(icons[1].style.fill, "#0000ff"); - assertColorsMatch(icons[2].style.fill, "#a6a6a6"); - }); - - it("click selects corresponding item", () => { - d3Click.call(icons[0], icons[0], 0, 0); - - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#a6a6a6"); - assertColorsMatch(icons[2].style.fill, "#a6a6a6"); - }); - - it("ctrl+click adds item to current selection", () => { - d3Click.call(icons[0], icons[0], 0, 0, ClickEventType.CtrlKey); - - assertColorsMatch(icons[0].style.fill, "#ff0000"); - assertColorsMatch(icons[1].style.fill, "#0000ff"); - assertColorsMatch(icons[2].style.fill, "#a6a6a6"); - }); - }); - }); - it("legend defaults", () => { let legendArray = getLotsOfLegendData(), legendData: LegendData = { dataPoints: legendArray, title: "" }, @@ -888,208 +739,9 @@ describe("legend", () => { } }); - describe("Mobile: interactive legend DOM validation", () => { - let element: HTMLElement, - viewport: powerbi.IViewport, - legend: ILegend, - colorStyle = "color: {0};", - defaultLegendHeight = 70, - interactivityService: IInteractivityService; - - let legendData: LegendDataPoint[] = [ - { - category: "state", - label: "Alaska", - color: "red", - - measure: 0, - identity: createSelectionIdentity(), - selected: false - }, - { - category: "state", - label: "California", - color: "blue", - - measure: 5, - identity: createSelectionIdentity(), - selected: false - }, - { - category: "state", - label: "Texas", - color: "green", - - measure: 10, - identity: createSelectionIdentity(), - selected: false - }, - ]; - - beforeEach(() => { - element = testDom("500", "500"); - interactivityService = createInteractivityService(createVisualHost({})); - legend = createLegend(element, true, interactivityService); - }); - - describe("3 item legend", () => { - it("legend dom validation one legend item count validation", (done) => { - legend.drawLegend({ - dataPoints: [ - legendData[1], - ] - }, viewport); - - setTimeout(() => { - expect(element.querySelectorAll(".interactive-legend .title").length).toBe(1); - expect(element.querySelectorAll(".interactive-legend .item").length).toBe(1); - done(); - }, DefaultWaitForRender); - }); - - it("legend dom validation three legend items count validation", (done) => { - legend.drawLegend({ dataPoints: legendData }, viewport); - - setTimeout(() => { - expect(element.querySelectorAll(".interactive-legend .title").length).toBe(1); - expect(element.querySelectorAll(".interactive-legend .item").length).toBe(3); - done(); - }, DefaultWaitForRender); - }); - - it("legend dom validation three legend items first item name and measure", (done) => { - legend.drawLegend({ dataPoints: legendData }, viewport); - - setTimeout(() => { - expect(element.querySelector(".interactive-legend .title").textContent).toBe(legendData[0].category); - expect(element.querySelector(".interactive-legend .item .itemName").textContent.trim()).toBe("Alaska"); - expect(element.querySelector(".interactive-legend .item .itemMeasure").textContent.trim()).toBe("0"); - done(); - }, DefaultWaitForRender); - }); - - it("legend dom validation three legend items last item name and measure", (done) => { - legend.drawLegend({ dataPoints: legendData }, viewport); - setTimeout(() => { - expect(element.querySelector(".interactive-legend .title").textContent).toBe(legendData[0].category); - expect(element.querySelectorAll(".interactive-legend .item .itemName")[legendData.length - 1].textContent.trim()).toBe("Texas"); - expect(element.querySelectorAll(".interactive-legend .item .itemMeasure")[legendData.length - 1].textContent.trim()).toBe("10"); - done(); - }, DefaultWaitForRender); - }); - - it("legend dom validation three legend items colors count", (done) => { - legend.drawLegend({ dataPoints: legendData }, viewport); - - setTimeout(() => { - expect(element.querySelectorAll(".interactive-legend .icon").length).toBe(3); - done(); - }, DefaultWaitForRender); - }); - - it("legend getHeight empty", () => { - expect(legend.getMargins().height).toBe(defaultLegendHeight); - }); - - it("legend getHeight no data", () => { - legend.drawLegend({ dataPoints: [] }, viewport); - - expect(legend.getMargins().height).toBe(defaultLegendHeight); - }); - - it("legend getHeight data", () => { - legend.drawLegend({ dataPoints: legendData }, viewport); - - expect(legend.getMargins().height).toBe(defaultLegendHeight); - }); - - it("legend getHeight one data point", () => { - legend.drawLegend({ - dataPoints: [ - legendData[0] - ] - }, viewport); - - expect(legend.getMargins().height).toBe(defaultLegendHeight); - }); - - it("legend dom validation incremental build", (done) => { - // Draw the legend once with the 3 states - let initialData: LegendDataPoint[] = legendData; - - legend.drawLegend({ dataPoints: initialData }, viewport); - - setTimeout(() => { - validateLegendDOM(initialData); - - // Draw the legend against with a new state at the start - let updatedData: LegendDataPoint[] = [ - legendData[0], - legendData[1], - legendData[2], - { - category: "state", - label: "Washington", - color: "orange", - - measure: 15, - identity: createSelectionIdentity(2), - selected: false - } - ]; - legend.reset(); - legend.drawLegend({ dataPoints: updatedData }, viewport); - setTimeout(() => { - validateLegendDOM(updatedData); - done(); - }, DefaultWaitForRender); - }, DefaultWaitForRender); - }); - - }); - - function validateLegendDOM(expectedData: LegendDataPoint[]): void { - let len = expectedData.length, - items = element.querySelectorAll(".interactive-legend .item"); - - expect(element.querySelectorAll(".interactive-legend .title").length).toBe(1); - expect(items.length).toBe(len); - - let icons = element.querySelectorAll(".interactive-legend .icon"); - expect(icons.length).toBe(len); - - // items are returned from the table, first row and then second row. - // rearrage it to match the way the legend outputs it: by columns. - let rearrangedItems = [], - rearrangedIcons = []; - - for (let i = 0; i < len; i++) { - rearrangedItems.push(items[i]); - rearrangedIcons.push(icons[i]); - } - - for (let i = 0; i < len; ++i) { - let expectedDatum = expectedData[i], - item = rearrangedItems[i], - icon = rearrangedIcons[i]; - - expect(item.querySelector(".itemName").textContent).toBe(expectedDatum.label); - expect(item.querySelector(".itemMeasure").textContent.trim()).toBe(expectedDatum.measure.toString()); - - let color = icon - .getAttribute("style") - .substring(icon.getAttribute("style").indexOf("color:")) - .trim(); - - expect(color).toBe(stringExtensions.format(colorStyle, expectedDatum.color)); - } - } - }); - describe("SVGLegend DOM", () => { let element: HTMLElement, legend: ILegend, - interactivityService: IInteractivityService, viewport: powerbi.IViewport = { height: 100, width: 500, @@ -1127,9 +779,8 @@ describe("legend", () => { beforeEach(() => { element = testDom("500", "500"); - interactivityService = createInteractivityService(createVisualHost({})); - legend = createLegend(element, false, interactivityService); + legend = createLegend(element, false); }); it("should render 3 legendText elements", (done) => { diff --git a/test/mocks/mockBehavior.ts b/test/mocks/mockBehavior.ts deleted file mode 100644 index e6dbd94..0000000 --- a/test/mocks/mockBehavior.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* -* Power BI Visualizations -* -* Copyright (c) Microsoft Corporation -* All rights reserved. -* MIT License -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the ""Software""), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -import { interactivitySelectionService, interactivityBaseService } from "powerbi-visuals-utils-interactivityutils"; - -import SelectableDataPoint = interactivitySelectionService.SelectableDataPoint; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; -import ISelectionHandler = interactivityBaseService.ISelectionHandler; - -export default class MockBehavior implements IInteractiveBehavior { - private selectableDataPoints: SelectableDataPoint[]; - private selectionHandler: ISelectionHandler; - - constructor(selectableDataPoints: SelectableDataPoint[]) { - this.selectableDataPoints = selectableDataPoints; - } - - public bindEvents(options: any, selectionHandler: ISelectionHandler): void { - this.selectionHandler = selectionHandler; - } - - /** - * Stub method to spy on - */ - public renderSelection(hasSelection: boolean): void { } - - public selectIndex(index: number, multiSelect?: boolean): void { - this.selectionHandler.handleSelection(this.selectableDataPoints[index], !!multiSelect); - } - - public select(datapoint: SelectableDataPoint, multiSelect?: boolean): void { - this.selectionHandler.handleSelection(datapoint, !!multiSelect); - } - - public clear(): void { - this.selectionHandler.handleClearSelection(); - } - - public selectIndexAndPersist(index: number, multiSelect?: boolean): void { - this.selectionHandler.handleSelection(this.selectableDataPoints[index], !!multiSelect); - } - - public verifyCleared(): boolean { - let selectableDataPoints: SelectableDataPoint[] = this.selectableDataPoints; - - for (let i = 0, ilen = selectableDataPoints.length; i < ilen; i++) { - if (selectableDataPoints[i].selected) - return false; - } - - return true; - } - - public verifySingleSelectedAt(index: number): boolean { - let selectableDataPoints: SelectableDataPoint[] = this.selectableDataPoints; - - for (let i = 0, ilen = selectableDataPoints.length; i < ilen; i++) { - let dataPoint: SelectableDataPoint = selectableDataPoints[i]; - - if (i === index) { - if (!dataPoint.selected) { - return false; - } - } - else if (dataPoint.selected) { - return false; - } - } - - return true; - } - - public verifySelectionState(selectionState: boolean[]): boolean { - let selectableDataPoints: SelectableDataPoint[] = this.selectableDataPoints; - - for (let i = 0, ilen = selectableDataPoints.length; i < ilen; i++) { - if (selectableDataPoints[i].selected !== selectionState[i]) { - return false; - } - } - - return true; - } - - public selections(): boolean[] { - let selectableDataPoints: SelectableDataPoint[] = this.selectableDataPoints, - selections: boolean[] = []; - - for (let dataPoint of selectableDataPoints) { - selections.push(!!dataPoint.selected); - } - - return selections; - } -} diff --git a/test/mocks/mockOpacityBehavior.ts b/test/mocks/mockOpacityBehavior.ts deleted file mode 100644 index 9cd7907..0000000 --- a/test/mocks/mockOpacityBehavior.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* -* Power BI Visualizations -* -* Copyright (c) Microsoft Corporation -* All rights reserved. -* MIT License -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the ""Software""), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -import { interactivityBaseService, interactivitySelectionService } from "powerbi-visuals-utils-interactivityutils"; - -import SelectableDataPoint = interactivitySelectionService.SelectableDataPoint; -import IInteractiveBehavior = interactivityBaseService.IInteractiveBehavior; -import ISelectionHandler = interactivityBaseService.ISelectionHandler; -import OpacityLegendBehavior from "../../src/legend/behavior/opacityLegendBehavior"; - -export default class MockOpacityBehavior extends OpacityLegendBehavior implements IInteractiveBehavior { - protected legendIcons: any; - private selectableDataPoints: SelectableDataPoint[]; - private selectionHandler: ISelectionHandler; - - public uploadPoints(selectableDataPoints: SelectableDataPoint[]) { - this.selectableDataPoints = selectableDataPoints; - } - - public bindEvents(options: any, selectionHandler: ISelectionHandler): void { - this.selectionHandler = selectionHandler; - super.bindEvents(options, selectionHandler); - } - - public selectIndex(index: number, multiSelect?: boolean): void { - this.selectionHandler.handleSelection(this.selectableDataPoints[index], !!multiSelect); - } -} \ No newline at end of file