Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Get certified in Microsoft Fabric—for free! For a limited time, the Microsoft Fabric Community team will be offering free DP-600 exam vouchers. Prepare now

Reply
Anonymous
Not applicable

Add tooltip to a custom Visual

Hello everyone!

 

I developed a custom visual and I want to add tooltips. I have installed the package like described in the Microsoft documentation. If I want to add a mouse event with the tooltips to the dots I get this error:

benmangel_0-1638364148298.png

 Here is my code, if I forgot an import statement or something else is totally wrong, I am happy to know about 🙂

 

Thank you already in advance 😄

 

/*
 *  Power BI Visual CLI
 *
 *  Copyright (c) Microsoft CorporationAll 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.
 */

'use strict';

import 'core-js/stable';
import 'regenerator-runtime/runtime'; /* Right here */
import '../style/visual.less';
import './../style/visual.less';
import powerbi from 'powerbi-visuals-api';
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
import VisualObjectInstance = powerbi.VisualObjectInstance;
import DataView = powerbi.DataView;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import ISandboxExtendedColorPalette = powerbi.extensibility.ISandboxExtendedColorPalette;
import { VisualSettings } from './settings';
import { valueFormatter } from "powerbi-visuals-utils-formattingutils";
import { createTooltipServiceWrapper } from "powerbi-visuals-utils-tooltiputils";
import VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem;
import ITooltipService = powerbi.extensibility.ITooltipService;



import * as d3 from 'd3';
import { DateTimeUnit } from 'powerbi-visuals-utils-formattingutils/lib/src/formattingService/iFormattingService';

// Build Interface for Data Structure

interface ILineChartRow {
    milestone: string,
    report_date: Date,
    vor_ende: Date,
    tooltip: VisualTooltipDataItem[];
            
}

// Interface for diagonale

interface IDiagonal {
    begin: Date
    end: Date
}

export class Visual implements IVisual {
    private target: HTMLElement;
    private settings: VisualSettings;
    private container: d3.Selection<HTMLDivElement, any, HTMLDivElement, any>; // private container: d3.Selection<any, any, any, any>;???
    private svg: d3.Selection<SVGElement, any, any, any>



constructor(options: VisualConstructorOptions) {

    console.log('Visual constructor', options);
    this.target = options.element;

    /** Create the chart container when the visual loads */
    this.container = d3.select(this.target)
        .append('div')
            .attr('id', 'my_dataviz');
    
    createTooltipServiceWrapper(
        options.host.tooltipService,
        options.element
        )   
    }

        

public update(options: VisualUpdateOptions) {
        console.log('Visual update', options);
    this.settings = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
          
    /** Clear down existing plot */
    this.container.selectAll('*').remove();

    /** Test 1: Data view has both fields added */
    let dataViews = options.dataViews;
    console.log('Test 1: Valid data view...');
    if (!dataViews
        || !dataViews[0]
        || !dataViews[0].table
        || !dataViews[0].table.rows
        || !dataViews[0].table.columns
        || !dataViews[0].metadata
    ) {
    console.log('Test 1 FAILED. No data to draw table.');
    return;
    }

    let table = dataViews[0].table
    //

     // Map Data in JSON Structure

    let data: ILineChartRow[] = table.rows.map(
        (cat, idx) => (
            {
                milestone: <string>table.rows[idx][0],
                report_date: <Date>table.rows[idx][1],
                vor_ende: <Date>table.rows[idx][2],
                tooltip: [{
                    displayName: "Milestone",
                    value: `${table.rows[idx][0]}`
                }]
            }
        )
    ); 
   
    // Format Dates

    let parseDate = d3.timeParse("%d.%m.%Y")

    data.forEach(function(d: any){
        d.report_date = parseDate(d.report_date),
        d.vor_ende = parseDate(d.vor_ende)
    })

    // Create groups for every milestone

        let sumstat = d3.nest<ILineChartRow>()
                .key(d => d.milestone)
                .entries(data)
    
    // Check the result

    console.log(sumstat)

    // Add Project Begin and and from formatting pane 

    let diagonale: IDiagonal = {
        begin: parseDate(this.settings.dataPoint.projektStart),
        end: parseDate(this.settings.dataPoint.projektEnd)
    }
   
    // Set Dimensions 

    let margin = {top: 10, right: 30, bottom: 30, left: 60},
        width = options.viewport.width - margin.left - margin.right,
        height = options.viewport.height - margin.top - margin.bottom;

    // Append the svg object to the body of the page

    var svg = this.container
            .append('svg')
                .attr('width', width + margin.left + margin.right)
                .attr('height', height + margin.top + margin.bottom)
            .append('g')
                .attr('transform',
                    'translate(' + margin.left + ',' + margin.top + ')');

    // Set Minimum and Maximum Values for the axes

    /* let minDate_report_date = d3.min(data, function(d){ return d.report_date})
    let maxDate_report_date = d3.max(data, function(d){ return d.report_date}) */

    // Set Minimum and Maximum for the report date

    let minDate_report_date = parseDate(this.settings.dataPoint.projektStart)
    let maxDate_report_date = parseDate(this.settings.dataPoint.projektEnd)

    // Define x position

    let x = d3.scaleTime()
        .domain([minDate_report_date,maxDate_report_date])
        .range([0,width]);

    // Define y0 position

    let y0 = d3.scaleTime()
        .domain([minDate_report_date,maxDate_report_date])
        .range([height,0])

    // Define y1 position

    let y1 = d3.scaleTime()
        .domain([minDate_report_date,maxDate_report_date])
        .range([height,0])

        // Add diagonal Line first so it is behind the dots

    /* svg.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "darkgrey")
    .attr("stroke-width", 1.5)
    .attr("d", d3.line<ILineChartRow>()
        .x(function(d) {return x(d.report_date)})
        .y(function(d) { return y1(d.report_date)})) */

        svg.append("line")
        .attr("y1", y1(minDate_report_date))
        .attr("y2", y1(maxDate_report_date))
        .attr("x1", x(minDate_report_date))
        .attr("x2", x(maxDate_report_date))
        .style("stroke", "darkgrey")
        .attr("stroke-width", 1.5)

    // Add Milestone lines

     svg.selectAll(".line")
    .data(sumstat)
    .enter()
    .append("path")
        .attr("fill", "none")
        .attr("stroke", this.settings.dataPoint.fillLine)
        .attr("stroke-width", this.settings.dataPoint.thickness)
        .attr("d", function(d){
        return d3.line<ILineChartRow>()
            .x(function(d) { return x(d.report_date); })
            .y(function(d) { return y0(d.vor_ende); })
            (d.values)
        })

    // Add Milestone Dots

    svg.append('g')
    .selectAll("dot")
    .data(data)
    .enter()
    .append("circle")
        .attr("cx", function (d) { return x(d.report_date); } )
        .attr("cy", function (d) { return y0(d.vor_ende); } )
        .attr("r", this.settings.dataPoint.thickness * 1.15)
        .style("fill", this.settings.dataPoint.fillLine)
        // HERE OCCURS THE ERROR
        .on("mouseover", (d) => {
            let mouse = d3.mouse(this.svg.node())
            let x = mouse[0]
            let y = mouse[]
        })

    // Add rectangle to hide something

    svg.append("rect")
        .attr("x", 0 - margin.left)
        .attr("y", 0)
        .attr("width", margin.left)
        .attr("height", options.viewport.height)
        .attr("fill", "white")

    // Add X axis

    svg.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x));

    // Add Y0 axis
    
    svg.append("g")
        .call(d3.axisLeft(y0))

    // Add Y1 axis
    
    svg.append("g")
        .attr("transform", "translate( " + width + ", 0 )")
        .call(d3.axisRight(y1) // Remove the axis ticks
            .ticks(0))
        .call(g => g.select(".domain")
            .remove()) // Remove the axis itself

// Loop iterates through sumstat object and places for every first Item of "Vorr_Ende" a textfield
// As text is the i version of the key of the sumstat object used

console.log("this is my sumstat *-*: ", sumstat)

// Labels placed next to y axis

/*  for ( let i = 0; i < Object.keys(sumstat).length; i++) {
    svg.append("g").selectAll("text")
        .data(sumstat)
        .enter()
        .append("text")
            .attr("x", 10)
            .attr("y", y1(sumstat[i].values[0].vor_ende)-2)
            .attr("font-size", 10)
            .style("fill", "darkgrey")
            .text(sumstat[i].key)
            }   
 */
// Labels placed at the end of the lines

 for ( let i = 0; i < Object.keys(sumstat).length; i++) {
    svg.append("g").selectAll("text")
        .data(sumstat)
        .enter()
        .append("text")
            .attr("x", x(sumstat[i].values[Object.keys(sumstat[i].values).length - 1].report_date) + 8)
            .attr("y", y1(sumstat[i].values[Object.keys(sumstat[i].values).length - 1].vor_ende) + 3)
            .attr("font-size", this.settings.dataPoint.radius) // gets infos from the settings.ts file
            .style("fill", this.settings.dataPoint.fillLabel)
            .text(sumstat[i].key)
    } 

   
}

    private static parseSettings(dataView: DataView): VisualSettings {
        return VisualSettings.parse(dataView) as VisualSettings;
    }

    /**
     * This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
     * objects and properties you want to expose to the users in the property pane.
     *
     */
     public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
        return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
    }
}
1 ACCEPTED SOLUTION
v-rzhou-msft
Community Support
Community Support

Hi @Anonymous 

Here is an example code to add tooltips to custom visual. Try it and compare it with your codes.

For reference: Sample code

 

...   
 private renderTooltip(selection: Selection<Line | Task | MilestonePath>): void {
        this.tooltipServiceWrapper.addTooltip(
            selection,
            (tooltipEvent: TooltipEventArgs<TooltipEnabledDataPoint>) => {
                return tooltipEvent.data.tooltipInfo;
            });
    }
...

 

Here is a post with similar requirement like yours.

For reference:

Custom Visual Tooltip Data Option

 

Best Regards,
Rico Zhou

 

If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

View solution in original post

1 REPLY 1
v-rzhou-msft
Community Support
Community Support

Hi @Anonymous 

Here is an example code to add tooltips to custom visual. Try it and compare it with your codes.

For reference: Sample code

 

...   
 private renderTooltip(selection: Selection<Line | Task | MilestonePath>): void {
        this.tooltipServiceWrapper.addTooltip(
            selection,
            (tooltipEvent: TooltipEventArgs<TooltipEnabledDataPoint>) => {
                return tooltipEvent.data.tooltipInfo;
            });
    }
...

 

Here is a post with similar requirement like yours.

For reference:

Custom Visual Tooltip Data Option

 

Best Regards,
Rico Zhou

 

If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

Helpful resources

Announcements
OCT PBI Update Carousel

Power BI Monthly Update - October 2024

Check out the October 2024 Power BI update to learn about new features.

September Hackathon Carousel

Microsoft Fabric & AI Learning Hackathon

Learn from experts, get hands-on experience, and win awesome prizes.

October NL Carousel

Fabric Community Update - October 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors