naver / billboard.js

📊 Re-usable, easy interface JavaScript chart library based on D3.js
https://naver.github.io/billboard.js/
MIT License
5.83k stars 351 forks source link

Custom Html ToolTip #1711

Closed BhavikThakkar closed 3 years ago

BhavikThakkar commented 3 years ago

Description

Custom tooltip using Html template

Steps to check or reproduce

I need to use custom html template in Tooltip which i will get from API which looks like below

'<style>'+ ' .Status_Black {'+ ' color: black;'+ ' font-family: Arial;'+ ' }'+ ' .white-bg{'+ ' background: white;'+ ' }'+ ''+ ' .Status_Red {'+ ' color: red;'+ ' font-family: Arial;'+ ' font-weight: bold;'+ ' }'+ ''+ ' .Status_Orange {'+ ' color: #FFA500;'+ ' font-family: Arial;'+ ' font-weight: bold;'+ ' }'+ ''+ ' .ColHeader {'+ ' color: black;'+ ' font-family: Arial;'+ ' font-weight: bold;'+ ' width: 25%;'+ ' }'+ ''+ ' .MeasureTable {'+ ' width: 250px;'+ ' background-color: gree;'+ ' margin: 0;'+ ' padding: 0;'+ ' border: 0;'+ ' }'+ ''+ ' .ColLeft {'+ ' text-align: left;'+ ' }'+ ''+ ' .ColRight {'+ ' text-align: right;'+ ' }'+ ''+ ' .ColCenter {'+ ' text-align: center;'+ ' }'+ ' .table{'+ ' border-collapse: collapse;'+ ' border: 1px solid black;'+ ' }'+ ' tr, td, th{'+ ' border: 1px solid black;'+ ' }'+ ' .table-header{'+ ' background: white;'+ ' }'+ ' '+ ' </style><div class="Status_Black ColLeft white-bg" bgcolor="white"> Date my wish ti show<mat-dialog-actions><button mat-button mat-dialog-close>Cancel</button><button id="modalBtnId" style="position: absolute;right: 12px;top: 4px;" class="filled-button mat-icon-button" mat-dialog-close><mat-icon class="mat-icon material-icons" style="line-height: 30px;" role="img" aria-hidden="true" onclick="closeTooltip()">close</mat-icon></button></mat-dialog-actions><br/> '+ ' Operator Skippy <br/> '+ ' <div class="ScrollTable"> '+ ' <table class="MeasureTable"><thead><tr><th class="ColHeader ColCenter">Head</th><th class="ColHeader ColCenter">Sample</th><th class="ColHeader ColCenter">Reading</th><th class="ColHeader ColCenter">Value</th></tr></thead><tfoot><tr><th></th><th></th><th></th><th></th></tr></tfoot><tbody><tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight"> 1</td><td class="Status_Black ColRight">36.78</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Black ColRight"> 1</td><td class="Status_Red ColRight"> 2</td><td class="Status_Black ColRight">26.93</td></tr><td class="Status_Black ColLeft"> A</td><td class="Status_Red ColRight"> 1</td><td class="Status_Black ColRight"> 3</td><td class="Status_Black ColRight">12.13</td></tr><td class="Status_Orange ColLeft"> A</td><td class="Status_Orange ColRight"> 1</td><td class="Status_Black ColRight"> 4</td><td class="Status_Black ColRight">37.47</td></tr></tbody></table></div></div>';

Problems

  1. Billboard is not accepting class similar as html , expecting in below format

{=CLASS_TOOLTIP} and same for style

Is there any way that billboard accept Html Directly witout any modification.

netil commented 3 years ago

Billboard is not accepting class similar as html , expecting in below format {=CLASS_TOOLTIP} and same for style

{=CLASS_TOOLTIP} is just a template syntax within the tooltip template string and it doesn't necessarily to be used always. You can add whatever class you want to the template HTML.

There're 2 ways to implement custom HTML:

BhavikThakkar commented 3 years ago

Thanks for your quick response.

our requirement is like below

"tooltip":{
    "format":{},
    contents: (d, defaultTitleFormat, defaultValueFormat, color) => {
        return this.GetTooptipData(chart,d);
    }
}

// Function to get data from API using promise

 private GetTooptipData(chart,d)
{
    return new Promise(resolve => {
            **API CAll  to featch Html** 
            resolve(this.tooltipDataValue); // it will resolve Html which get from API response
            }
        });
}

i am getting error with this implementation like " Type '(this: Chart, d: any, defaultTitleFormat: string, defaultValueFormat: string, color: any) => Promise' is not assignable to type '((this: Chart, data: any, defaultTitleFormat: string, defaultValueFormat: string, color: any) => string) | { bindto?: string | HTMLElement; template?: string; text?: { ...; }; }'."

we were using same implementation with C3.js chart before which was working perfectly fine but not will billboard.js , please suggest a way to achieve this

THANKS in ADVANCE .....

netil commented 3 years ago

The tooltip option isn't designed for async operation. In your use case, need to approach differently combining the tooltip event option.

For example you can call whatever async call within tooltip.onshow option and make tooltip HTML string from the returned Promise call.

bb.generate({
  data: {
    columns: [
      ["data1", 300, 350, 300, 0, 0, 0],
      ["data2", 130, 100, 140, 200, 150, 50]
    ],
    types: {
      data1: "area",
      data2: "area-spline"
    }
  },
  tooltip: {
    contents: () => "",
    onshow: function(data) {
      GetTooptipData().then(msg => {
        // make tooltip html here, from the returned promise result
        this.$.tooltip.html(`<h1>${msg}</h1>`);
      });
    }
  }
});

function GetTooptipData() {
  return new Promise(resolve => {
    resolve("my tooltip");
  });
}
BhavikThakkar commented 3 years ago

Thanks a lot , it works one last query for same

I got one onclick function but not sure weather it will allow to decide tooltip display type

image

BhavikThakkar commented 3 years ago

HI

Any update please?

netil commented 3 years ago

One of the approach you can do is add some flag variable to determine tooltip showing for react on mouseover event or click event. And then set data.onclick option(data.onclick works when data points is clicked) value for click type and tooltip.onshow option value for over type.

Take the below example code as reference how to approach for your needs.

// set true for 'over', false for 'click'
const isTooltipByOver = true;

const chart = bb.generate({
  data: {
    columns: [
      ["data1", 300, 350, 300, 0, 0, 0],
      ["data2", 130, 100, 140, 200, 150, 50]
    ],
    types: {
      data1: "area",
      data2: "area-spline"
    },
    onclick: !isTooltipByOver && function(d, element) {
        GetTooptipData();
    }
  },
  tooltip: {
    contents: () => "",
    onshow: isTooltipByOver && function(data) {
      GetTooptipData();
    }
  }
});

function GetTooptipData() {
    return new Promise(resolve => {
        resolve("my tooltip");
    }).then(msg => {
        // make tooltip html here, from the returned promise result
        chart.$.tooltip.html(`<h1>${msg}</h1>`);
    });
}
BhavikThakkar commented 3 years ago

HI Thanks for your response , it is working on stackbiz but not while I put it into my code , I am getting below error.

image

There is change in "Data" => types

` "types":{"Series 1":line(),"Series 2":line()},

If i Pass

types: { data1: "area", data2: "area-spline" },

I am getting below error

image

I am using angular for this and below is package.json

{
  "name": "fuse",
  "version": "6.2.4",
  "license": "https://themeforest.net/licenses/terms/regular",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --open",
    "start-hmr": "ng serve --configuration hmr --source-map=false --hmr-warning=false",
    "start-hmr-sourcemaps": "ng serve --configuration hmr --source-map=true --hmr-warning=false",
    "build": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --dev",
    "build-stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --dev --stats-json",
    "build-prod": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod",
    "build-prod-stats": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod --stats-json",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "bundle-report": "webpack-bundle-analyzer dist/stats.json",
    "compodoc": "npx compodoc -p src/tsconfig.app.json"
  },
  "private": true,
  "dependencies": {
    "@agm/core": "1.0.0-beta.3",
    "@angular/animations": "6.0.9",
    "@angular/cdk": "6.3.3",
    "@angular/common": "6.0.9",
    "@angular/compiler": "6.0.9",
    "@angular/core": "6.0.9",
    "@angular/flex-layout": "6.0.0-beta.16",
    "@angular/forms": "6.0.9",
    "@angular/http": "6.0.9",
    "@angular/material": "6.3.3",
    "@angular/material-moment-adapter": "6.3.3",
    "@angular/platform-browser": "6.0.9",
    "@angular/platform-browser-dynamic": "6.0.9",
    "@angular/router": "6.0.9",
    "@ng-bootstrap/ng-bootstrap": "4.0.2",
    "@ngrx/effects": "6.0.1",
    "@ngrx/router-store": "6.0.1",
    "@ngrx/store": "6.0.1",
    "@ngrx/store-devtools": "6.0.1",
    "@ngx-translate/core": "10.0.2",
    "@ngx-translate/http-loader": "4.0.0",
    "@swimlane/dragula": "3.8.0",
    "@swimlane/ngx-charts": "8.1.0",
    "@swimlane/ngx-datatable": "13.0.1",
    "@swimlane/ngx-dnd": "4.0.0",
    "@types/c3": "0.6.0",
    "@types/d3": "5.0.0",
    "@types/d3-scale": "2.0.2",
    "@types/dragula": "2.1.34",
    "@types/prismjs": "1.9.0",
    "ag-grid": "18.1.2",
    "ag-grid-angular": "19.1.2",
    "ag-grid-community": "19.1.4",
    "angular-calendar": "0.25.2",
    "angular-in-memory-web-api": "0.6.0",
    "angular2-datetimepicker": "1.1.1",
    "billboard.js": "2.1.1",
    "c3": "0.6.6",
    "chart.js": "2.7.2",
    "classlist.js": "1.1.20150312",
    "core-js": "2.5.7",
    "d3": "5.7.0",
    "d3-scale": "2.1.2",
    "dom-to-image": "2.6.0",
    "flag-icon-css": "3.3.0",
    "hammerjs": "2.0.8",
    "jquery": "3.3.1",
    "lodash": "4.17.10",
    "moment": "2.22.2",
    "ng-drag-drop": "5.0.0",
    "ng-pick-datetime": "6.0.16",
    "ng2-charts": "1.6.0",
    "ngrx-store-freeze": "0.2.4",
    "ngx-clipboard": "11.1.9",
    "ngx-color-picker": "6.5.0",
    "ngx-cookie-service": "1.0.10",
    "ngx-daterangepicker-material": "1.2.4",
    "ngx-material-timepicker": "2.9.0",
    "ngx-toastr": "8.8.0",
    "perfect-scrollbar": "1.4.0",
    "primeicons": "1.0.0",
    "primeng": "7.1.1",
    "prismjs": "1.15.0",
    "rxjs": "6.2.1",
    "rxjs-compat": "6.2.1",
    "sweetalert2": "7.33.1",
    "ts-md5": "1.2.4",
    "web-animations-js": "2.3.1",
    "zone.js": "0.8.26"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "0.6.8",
    "@angular/cli": "6.0.8",
    "@angular/compiler-cli": "6.0.9",
    "@angular/language-service": "6.0.9",
    "@angularclass/hmr": "2.1.3",
    "@types/jasmine": "2.8.8",
    "@types/jasminewd2": "2.0.3",
    "@types/lodash": "4.14.111",
    "@types/node": "8.9.5",
    "codelyzer": "4.2.1",
    "jasmine-core": "2.99.1",
    "jasmine-spec-reporter": "4.2.1",
    "karma": "1.7.1",
    "karma-chrome-launcher": "2.2.0",
    "karma-coverage-istanbul-reporter": "2.0.1",
    "karma-jasmine": "1.1.2",
    "karma-jasmine-html-reporter": "0.2.2",
    "printd": "1.0.1",
    "protractor": "5.3.2",
    "ts-node": "5.0.1",
    "tslint": "5.9.1",
    "typescript": "2.7.2",
    "webpack-bundle-analyzer": "2.13.1"
  }
}

`

netil commented 3 years ago

if you're using in ESM environment, you need to import shape modules. If you're using in UMD environment, you need to specify data types as string.

// UMD
data: {
  types: {
    data1: "bar",
    data2: "spline"
  }
}

// Generate chart by importing ESM
// Import types to be used only, where this will make smaller bundle size.
import bb, {
  area,
  areaLineRange,
  areaSpline,
  areaSplineRange,
  areaStep,
  bar,
  bubble,
  donut,
  gauge,
  line,
  pie,
  radar,
  scatter,
  spline,
  step
}

bb.generate({
  ...,
  data: {
    types: {
      data1: bar(),
      data1: spline()
    }
  }
});

Checkout the API doc

ThakkarKhushbu commented 3 years ago
  1. we need to apply dynamic point color for line chart. is there any way to do so?

we were doing customizationn in C3 to give custom color ,size to point like below

var circleHtml = '<circle r=' + point.Point_Size + ' ' + 'style="fill:' + point.Color_Fill + ';stroke-width:' + point.Border_Width + ';stroke:' + point.Color_Point + '" cx=' + hx + ' cy="' + hy + '" />' + '<text x="' + hx + '"y="' + hy + '"font-size=' + point.Point_Size + ' fill=' + point.Color_Text + '> ' + point.Point_Code + '</text>';

  1. we need all point shape diferrent in single series

image

here point pattern is series wise , how we can give it at point level?

BhavikThakkar commented 3 years ago

HI

any update please ?

netil commented 3 years ago

if you need to customize data points, there're 2 ways on doing that

chart.$.circles.each(function() { // 'this' will refer data point element this.style.stroke = "red"; this.style.strokeWidth = "red"; });

- or set css rule for circles
```css
circle.bb-circle {
   stroke: red !important;
   fill: red !important;
}

and plz, read the doc API or the example code for the detail customization.

BhavikThakkar commented 3 years ago

HI @netil

my question is "Can't we achieve this without customization?"

1) Custom Point Shapes 2) Different point shape for single series ? 3) same for legend symbol?

we are thinking to move to billboard to avoid customization we did in C3 if billboard allows above features

netil commented 3 years ago
BhavikThakkar commented 3 years ago

Thanks :)

BhavikThakkar commented 3 years ago

HI @netil I made customization in points but after that click of data stop working

image

BhavikThakkar commented 3 years ago

No, custom point shape is applied to all data series, not for point by point. You can make to have each data points having different one, but need to do your own customization.

for this

BhavikThakkar commented 3 years ago

image single series with multiple point shape