chartjs-plugin-annotation: Cannot set properties of undefined (setting 'display') at resolveLabelElementProperties

We seem to be stuck with plugin-annotation v1.3.0 and chartjs v3.7.0 where following example for a line chart with multiple datasets works with multiple annotations.

https://codepen.io/dbauszus-glx/pen/MWOjGzw

Upgrading plugin-annotation to v2.0.0 causes following TypeError: Cannot set properties of undefined (setting 'display') at resolveLabelElementProperties.

Upgrading chartjs to v3.9.1 causes a different TypeError: Cannot read properties of undefined (reading 'lastIndexOf')

The same error persists with chartjs on v3.9.1 and plugin-aanotation on v2.0.0

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 18

Most upvoted comments

I’m trying to use this plugin in an Ember (4.x) app, and I’ve run across this issue as well. From what I can tell, the issue for me stems from the fact that:

  1. ember-auto-import generates the following code for Webpack to compile:
    d('chart.js/auto', [], function() { return require('chart.js/auto'); });
    d('chartjs-plugin-annotation', [], function() { return require('chartjs-plugin-annotation'); });
  1. Webpack then apparently maps:
    • chart.js/auto (version 4.1.x) to chart.js/auto/auto.cjs (the CommonJS module), which then imports chart.js/dist/chart.cjs, while
    • chartjs-plugin-annotation (version 2.1.1) gets mapped to chartjs-plugin-annotation/dist/chartjs-plugin-annotation.esm.js (the ESM module), which then imports chart.js/dist/chart.js.
  2. Because they import Chart.js via different files, then two separate copies of Chart.js end up in the bundle, and this plugin ends up registering its elements in the “wrong” version of Chart.js.
  3. And because the elements are not registered in the version of Chart.js that is used by the app, then the various defaults for config resolution are missing, and the plugin fails with an error like:
main.js:1564 Uncaught TypeError: Cannot read properties of undefined (reading 'borderWidth')
    at resolveLabelElementProperties (main.js:1564:31)
    at LineAnnotation.resolveElementProperties (main.js:1380:29)
    at updateElements (main.js:2082:32)
    at Object.afterUpdate (main.js:2249:5)
    at Object.callback (main.js:14474:19)
    at PluginService._notify (main.js:8082:33)
    at PluginService.notify (main.js:8065:29)
    at Chart.notifyPlugins (main.js:9320:30)
    at Chart.update (main.js:8881:14)
    at Chart.<anonymous> (main.js:8623:64)

Minimal Repro

I am able to reproduce the issue in a minimal project with:

{
  "dependencies": {
    "chart.js": "^4.1.2",
    "chartjs-plugin-annotation": "^2.1.1"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1"
  }
}

using the command line:

npx webpack --mode development  --devtool source-map ./index.js

and the following index.js file modeled off the basic line sample:

const Chart = require('chart.js/auto');
const annotationPlugin = require('chartjs-plugin-annotation');
Chart.register(annotationPlugin.default);

// From https://www.chartjs.org/chartjs-plugin-annotation/latest/samples/line/basic.html
const DATA_COUNT = 8;

const labels = [];
for (let i = 0; i < DATA_COUNT; ++i) {
    labels.push('' + i);
}

const data = {
    labels: labels,
    datasets: [{
        data: [ 57.726, 91.99, 45.051, 64.686, 45.34, 88.123, 30.814, 27.856 ] // Utils.numbers(numberCfg)
    }]
};

const annotation = {
    type: 'line',
    borderColor: 'black',
    borderWidth: 3,
    scaleID: 'y',
    value: 50
};

const config = {
    type: 'line',
    data,
    options: {
        scales: {
            y: {
                stacked: true
            }
        },
        plugins: {
            annotation: {
                annotations: {
                    annotation
                }
            }
        }
    }
};

new Chart(document.getElementById('canvas'), config);

And the following index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Reproduce chartjs-plugin-annotation#786</title>
</head>
<body>
<canvas id="canvas" width="400"></canvas>
<script src="dist/main.js"></script>
</body>
</html>

Analysis

Obviously the issue is that Webpack is choosing inconsistent module types for chart.js vs this plugin.

If I replace the first lines with:

import Chart from 'chart.js/auto';
import annotationPlugin from 'chartjs-plugin-annotation';
Chart.register(annotationPlugin);

Then the issue goes away, because Webpack uses the ESM versions of each.

It’s clear to me why Webpack is choosing the CJS when using require('chart.js/auto') but it was less clear to me why it was using ESM for require('chartjs-plugin-annotation'). Probably because it prefers module to main by default?

Fortunately I just noticed that #832 was merged recently, and if I apply that change to my local node_modules/chartjs-plugin-annotation/package.json, then this issue is resolved because Webpack correctly chooses chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js, which then uses require("chart.js"), which Webpack maps to chart.js/dist/chart.cjs, thereby eliminating the inconsistent versions.

So I think this issue (at least for me) should be resolved with the next version of this plugin that includes #832.

As a workaround (for now or in perpetuity), I can introduce an intermediate ESM module that uses import for all Chart.js plugins, so that Webpack properly and consistently chooses the ESM version of each.

hey @stockiNail, I’ve just tried to reproduce this in a sandbox, but it does work there.

In my setup, I’m generating the charts on the backend with nodejs (using ChartJSNodeCanvas). Probably that’s something on their side then…

Sorry for the late reply as I was on holidays. I just checked this now and chart 3.9.1 with plugin annotation 2.0.1 works as expected if imported through skypack. Thanks for looking into this.