Is it possible to have just one custom datalabel on a stacked waterfall with Highcharts?

Viewed 20

I am using the react wrapper for high charts btw.

What I have currently.

My stacked waterfall chart

What I'm aiming for with but stacked waterfall chart

enter image description here

Just started using high charts and I love it but this one has me stumped. I figured out the data that I need to put that but now I just need to be able to place it in there..

Possible avenues of approach: Is there a way to add custom HTML only for the top series? Could I alter the data set to maybe re-render with the top label? Could I just make the labels show but add an additional label above with the points I wants and make the rest transparent?


import React from "react";

import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";
import HC_more from "highcharts/highcharts-more";
HC_more(Highcharts);

let categories = ["bar 1", "bar 2", "bar 3", "bar 4", "total bar"];

let series = [
  {
    data: [
      20,
      10,
      -15,
      30,
      {
        isSum: true,
      },
    ],
    name: "custom series 1",
  },
  {
    data: [
      20,
      50,
      -25,
      10,
      {
        isSum: true,
      },
    ],
    name: "custom series 2",
    lineWidth: 0,
  },
  {
    data: [
      5,
      10,
      -5,
      10,
      {
        isSum: true,
      },
    ],
    name: "custom series 3",
    lineWidth: 0,
  },
];
let grandTotal = true;

function _createLabelValuePairs(s, c) {
  let collection = {};

  //Step 0 - iterate over data to get the series
  s.map((dataSet) => {
    //since we have total bars. Something that shouldn't be calc into the dataset we still need to figure out how many series it has
    //for all the set values
    dataSet.data.map((item, idx) => {
      if (typeof item == "number") {
        if (collection[c[idx]]) {
          collection[c[idx]]["values"].push(item);
        } else {
          collection[c[idx]] = {};
          collection[c[idx]]["values"] = [];
          collection[c[idx]]["values"].push(item);
        }
      }
    });
  });

  //Step 1 - Get my totals for each dataset
  for (const a in collection) {
    collection[a]["barTotal"] = collection[a].values.reduce(
      (partialSum, a) => partialSum + a,
      0,
    );
  }

  //Step 2 - Get grand total numbers of all datasets.
  if (grandTotal) {
    //Step 2a - Totals for each bar
    let sum = 0;

    for (const item in collection) {
      sum = sum + collection[item].barTotal;
      if (collection[c[c.length - 1]]) {
        collection[c[c.length - 1]]["values"].push(collection[item].barTotal);
      } else {
        collection[c[c.length - 1]] = {};
        collection[c[c.length - 1]]["values"] = [];
        collection[c[c.length - 1]]["values"].push(collection[item].barTotal);
      }
    }
    //grand total bar will be the last entry in the set you pass in
    collection[c[c.length - 1]]["barTotal"] = sum;

    //Step 2b - Totals for each series
    options.series.map((item) => {
      if (collection[c[c.length - 1]]["seriesTotal"]) {
        collection[c[c.length - 1]]["seriesTotal"][
          item["name"]
        ] = item.data.reduce((partialSum, a) => {
          if (typeof partialSum == "number") {
            return Number(partialSum) + a;
          }
        }, 0);
      } else {
        collection[c[c.length - 1]]["seriesTotal"] = {};
        collection[c[c.length - 1]]["seriesTotal"][item["name"]] = 0;
        collection[c[c.length - 1]]["seriesTotal"][
          item["name"]
        ] = item.data.reduce((partialSum, a) => {
          if (typeof partialSum == "number") {
            return Number(partialSum) + a;
          }
        }, 0);
      }
      if (
        collection[c[c.length - 1]]["seriesTotal"][item["name"]].indexOf(
          "[object Object]",
        )
      ) {
        collection[c[c.length - 1]]["seriesTotal"][item["name"]] = Number(
          collection[c[c.length - 1]]["seriesTotal"][item["name"]].split(
            "[object Object]",
          )[0],
        );
      }
    });
  }
  return collection;
}

function _getDelta(key, sets) {
  console.log(sets);
  //not passing anything back into the datalabel
  return null;
}

const options = {
  chart: {
    type: "waterfall",
    className: "test",
    showAxes: false,
  },
  colors: ["#00A9F4", "#B3B3B3", "#000000"],
  legend: {
    align: "right",
    symbolRadius: 0,
    verticalAlign: "top",
  },
  title: {
    text: "Stacked waterfall example",
  },
  plotOptions: {
    series: {
      stacking: "normal",
    },
    waterfall: {
      borderColor: "none",
      dashStyle: "Solid",
      dataLabels: {
        useHTML: true,
        className: "waterfall-label",
        enabled: true,
        formatter: function(a) {
          return _getDelta(
            this.key,
            _createLabelValuePairs(series, categories),
          );
        },
        inside: false,
      },
      states: {
        hover: {
          animation: {
            duration: 0,
          },
          enabled: false,
        },
        inactive: {
          enabled: false,
        },
        select: {
          enabled: false,
        },
      },
    },
  },
  xAxis: {
    categories: categories,
    labels: {
      style: {
        color: "#000000",
        cursor: "default",
        fontSize: "14px",
        fontFamily: "Mckinsey Sans Regular",
        width: "75px",
        whiteSpace: "normal", //set to normal
      },
    },
  },
  yAxis: {
    visible: false,
  },
  series: series,
  credits: {
    enabled: false,
  },
  tooltip: {
    animation: false,
    backgroundColor: "#333333",
    borderColor: "inherit",
    borderRadius: 0,
    formatter: function() {
      return this.x + ": " + this.y;
    },
    hideDelay: 0,
    shadow: false,
    style: {
      color: "white",
    },
  },
};

function Waterfall() {
  return <HighchartsReact highcharts={Highcharts} options={options} />;
}

export default Waterfall;

And if you want to know what all that code was for _createLabelPairs it's just to get the totals of the series and that of the bars as well( If I need to use those later). I can figure out the percentages with barTotals and total property in the total bar

result from _createLabelPairs function enter image description here

0 Answers
Related