ChartJS as a custom component

Good Day all,

Ive built a chart that I would like to incorporate into a custom component in UI Builder.

Please see code bellow,

<!doctype html>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Consuption_Status</title>

<style>

  * {

    margin: 0;

    padding: 0;

    font-family: sans-serif;

  }

  .chartMenu {

    width: 100vw;

    height: 40px;

    background: #1A1A1A;

    color: rgba(255, 100, 0, 1);

  }

  .chartMenu p {

    padding: 10px;

    font-size: 20px;

  }

  .chartCard {

    width: 100vw;

    height: calc(100vh - 40px);

    background: rgba(0, 0, 0, 1);

    display: flex;

    align-items: center;

    justify-content: center;

  }

  .chartBox {

    width: 700px;

    padding: 0px;

    border-radius: 20px;

    border: none;

    background: rgba(0, 0, 0, 0.0);

  }

</style>
<div class="chartMenu">

  <p>FleetinForm</p>

</div>

<div class="chartCard">

  <div class="chartBox">

    <canvas id="myChart"></canvas>

  </div>

</div>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<script>

// setup

const data = {

  labels: ['5%', '10%', '15%', '20%', '25%', '30%', '35%', '40%', '45%', '50%', '55%', '60%', '65%', '70%', '75%', '80%', '85%', '90%', '95%', '100%', '105%', '110%', '115%', '120%', '125%', '130%', '135%', '140%', '145%', '150%', '155%', '160%', '165%', '170%', '175%', '180%', '185%', '190%', '195%', '200%'],

  datasets: [{

    label: 'Weekly Sales',

    data: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,5, 5, 5, 5, 5, 5, 5],

    backgroundColor: [

    'rgba(255, 0, 0, 0.8)',

    'rgba(255, 12.75, 0, 0.8)',

    'rgba(255, 25.5, 0, 0.8)',

    'rgba(255, 38.25, 0, 0.8)',

    'rgba(255, 51, 0, 0.8)',

    'rgba(255, 63.75, 0, 0.8)',

    'rgba(255, 76.5, 0, 0.8)',

    'rgba(255, 89.25, 0, 0.8)',

    'rgba(255, 102, 0, 0.8)',

    'rgba(255, 114.75, 0, 0.8)',

    'rgba(255, 127.50, 0, 0.8)',

    'rgba(255, 140.25, 0, 0.8)',

    'rgba(255, 153, 0, 0.8)',

    'rgba(255, 165.75, 0, 0.8)',

    'rgba(255, 178.5, 0, 0.8)',

    'rgba(255, 191.25, 0, 0.8)',

    'rgba(255, 204, 0, 0.8)',

    'rgba(255, 216.75, 0, 0.8)',

    'rgba(255, 229.50, 0, 0.8)',

    'rgba(255, 242.25, 0, 0.8)',

    'rgba(255, 255, 0, 0.8)',

    'rgba(255, 255, 0, 0.8)',

    'rgba(242.25, 255, 0, 0.8)',

    'rgba(229.5, 255, 0, 0.8)',

    'rgba(216.75, 255, 0, 0.8)',

    'rgba(204, 255, 0, 0.8)',

    'rgba(191.25, 255, 0, 0.8)',

    'rgba(178.50, 255, 0, 0.8)',

    'rgba(165.75, 255, 0, 0.8)',

    'rgba(153, 255, 0, 0.8)',

    'rgba(140.25, 255, 0, 0.8)',

    'rgba(127.50, 255, 0, 0.8)',

    'rgba(114.75, 255, 0, 0.8)',

    'rgba(102, 255, 0, 0.8)',

    'rgba(89.25, 255, 0, 0.8)',

    'rgba(76.50, 255, 0, 0.8)',

    'rgba(63.75, 255, 0, 0.8)',

    'rgba(51, 255, 0, 0.8)',

    'rgba(38.28, 255, 0, 0.8)',

    'rgba(25.5, 255, 0, 0.8)',

        'rgba(0, 0, 0, 0.2)'

    ],

    borderColor: [

      'rgba(0, 0, 0, 0)'

    ],

NeedleValue: 50,

    borderWidth: 1,

cutout: ‘60%’,

circumference: 180,

rotation: 270

  }]

};

// gaugeNeedle block

const gaugeNeedle = {

id: 'gaugeNeedle',

afterDatasetDraw(chart, args, options) {

    const { ctx, config, data, chartArea: { top, bottom, left, right, width,

        height } } = chart;

   

    ctx.save();

    //console.log(data)

    const NeedleValue = data.datasets[0].NeedleValue;

    const dataTotal = data.datasets[0].data.reduce((a, b) => a + b, 0);

    const angle = Math.PI + (1 / dataTotal * NeedleValue * Math.PI);

    const cx = width / 2;

    const cy = chart._metasets[0].data[0].y;

    //console.log(ctx.canvas.offsetTop)

    // Needle

    ctx.translate(cx, cy);

    ctx.rotate(angle);

    ctx.beginPath();

    ctx.moveTo(0, -5);

    ctx.lineTo(chart._metasets[0].data[0].outerRadius, 0);

    ctx.lineTo(0, 5);

    ctx.fillStyle = 'rgba(255, 255, 255, 1)';

    ctx.fill();

               

    // Needle dot

    ctx.translate(-cx, -cy);

    ctx.beginPath();

    ctx.arc(cx, cy, 5, 5, 10);

    ctx.fill();

    ctx.restore();

    ctx.font = '25px Helvetica';

    ctx.fillStyle = 'rgba(255, 100, 0, 1)';

    ctx.fillText('Consumption Status (Target - 100%)', cx, cy + 60)

    ctx.textAlign = 'center';

    ctx.font = '50px Helvetica';

    ctx.fillStyle = 'rgba(255, 100, 0, 1)';

    ctx.fillText(NeedleValue +'%', cx, cy + 120)

    ctx.textAlign = 'center';

   

}

}

// config

const config = {

  type: 'doughnut',

  data,

  options: {

    plugins: {

    legend: {

        display: false

    },

    tooltip: {

        yAlign: 'bottom',

        displayColors: false,

        callbacks: {

            label: function(tooltipItem, data, value) {

                const tracker = tooltipItem.dataset.NeedleValue;

                return `Tracker Score: ${tracker} %`;

            }

        }

    }

}



},

plugins: [gaugeNeedle]

};

// render init block

const myChart = new Chart(

  document.getElementById('myChart'),

  config

);

</script>

I have tried various ways but have not succeeded, what am I missing? your assistance would be appreciated.

Kind Regards,
Raymond

Hello @Raymond_Woodley,

I see you posted a lot of HTML, but we do not have support for custom HTML.

Please see the example of implementing the chart using a custom component:

bundle.js
require({
  packages: [{
    name: 'highcharts',
    main: 'highcharts'
  }],
  paths   : {
    'highcharts': 'https://code.highcharts.com'
  }
});

define([
  'highcharts',
  'highcharts/highcharts-3d'
], (Highcharts) => {

  const DEFAULT_DATA = [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]

  function renderChart(el, { onSerieToggle }) {
    const chart = new Highcharts.Chart({
      chart      : {
        renderTo : el,
        type     : 'column',
        options3d: {
          enabled     : true,
          alpha       : 15,
          beta        : 15,
          depth       : 50,
          viewDistance: 25
        }
      },
      title      : {
        text: 'Chart rotation demo'
      },
      plotOptions: {
        column: {
          depth: 25
        },
        series: {
          events: {
            legendItemClick(e) {
              e.preventDefault();

              const serie = this;

              if (serie.visible) {
                serie.hide();
              } else {
                serie.show();
              }

              if (onSerieToggle) {
                onSerieToggle(serie.visible)
              }
            }
          }
        }
      },
      series     : [{
        data: []
      }]
    });

    return chart
  }

  return function MyChartComponent({ component, eventHandlers }) {
    const elRef = React.useRef(null)
    const chartRef = React.useRef(null)

    console.log('component', component)

    React.useEffect(() => {
      chartRef.current = renderChart(elRef.current, {
        onSerieToggle(isSerieVisible) {
          eventHandlers.onSeriesToggle({ isSerieVisible })
        }
      })
    }, [])

    React.useEffect(() => {
      const validData = []

      if (Array.isArray(component.data)) {
        component.data.forEach(item => {
          if (typeof item === 'number') {
            validData.push(item)
          }
        })
      }

      const data = validData.length
        ? validData
        : DEFAULT_DATA

      chartRef.current.series[0].setData(data);
      chartRef.current.redraw();
    }, [component.data, component.data?.length])

    React.useEffect(() => {
      chartRef.current.setTitle({ text: component.chartTitle });
    }, [component.chartTitle])

    return React.createElement('div', {
      ref  : elRef,
      style: { flex: 1, minHeight: '300px' }
    })
  }
});
component.json
{
  "name": "Chart Demo",
  "properties": [
    {
      "label": "Chart Title",
      "type": "text",
      "name": "chartTitle",
      "handlerId": "chartTitleLogic",
      "handlerLabel": "On Chart Title Logic",
      "handlerDescription": "This is a handler for getting Chart Title",
      "defaultValue": "Demo Chart"
    },
    {
      "label": "",
      "handlerId": "dataLogic",
      "handlerLabel": "Data Logic",
      "handlerDescription": "This is a handler for getting data for rendering in the chart",
      "type": "text",
      "name": "data"
    }
  ],
  "eventHandlers": [
    {
      "name": "onSeriesToggle",
      "label": "On Series Toggle",
      "contextBlocks": [
        {
          "_key": "multiple-element-7",
          "id": "isSerieVisible",
          "label": "Is Serie Visible"
        }
      ]
    }
  ],
  "showInToolbox": true,
  "id": "c_feb67d7c7871fa0d9217309fbc7ba30f",
  "type": "custom",
  "category": "Custom Components",
  "actions": [],
  "description": "",
  "faIcon": "pencil-ruler"
}

Hope that gives you an idea.
We are still working on documentation for custom components.

Regards,
Stanislaw