chartjs / chartjs/Chart.js

The documentation and type correctness of dataElementType

Open
#12,169 2 comments 0 reactions 0 assignees View on GitHub
type: bug
Dominant language
JavaScript
Stars
67.7k
Forks
11.9k
Avg merge
7h 39m
Merged PRs (30d)
5

Description

### Expected behavior

## Described behavior

According to the documentation [`docs/developers/charts.md`](https://github.com/chartjs/Chart.js/blob/master/docs/developers/charts.md?plain=1#L37) the chart `static defaults = { ... }` allow `dataElementType` to be `null` or `false` with the following comment:

> ```js
> defaults: {
> // If set to `false` or `null`, no dataset level element is created.
> // If set to a string, this is the type of element to create for the dataset.
> // For example, a line create needs to create a line element so this is the string 'line'
> datasetElementType: string | null | false,
>
> // If set to `false` or `null`, no elements are created for each data value.
> // If set to a string, this is the type of element to create for each data value.
> // For example, a line create needs to create a point element so this is the string 'point'
> dataElementType: string | null | false,
> }
> ```

and the type hints in [`src/types/index.d.ts`](https://github.com/chartjs/Chart.js/blob/master/src/types/index.d.ts#L672) allow the `dataElementType` to be even ~~undefined~~ unspecified:

> ```ts
>export interface DatasetControllerChartComponent extends ChartComponent {
> defaults: {
> datasetElementType?: string | null | false;
> dataElementType?: string | null | false;
> };
> }
> ```

although this might be wrong since `DatasetControllerChartComponent` is never used again in the whole codebase and just stays as an exported type.

If I do not specify `dataElementType` or set it to `null | false | undefined` the code should not crash unexpectedly (and should not allocate any arrays, create new objects, ... - unless parsing I guess?).

### Current behavior

```ts
class MyController extends DatasetController {
static id = 'my';
static defaults = {
dataElementType: null,
}
parse() { }
draw() { }
}
```

results in

```
core.registry.js:178 Uncaught Error: "null" is not a registered element.
at Registry._get (core.registry.js:178:13)
at Registry.getElement (core.registry.js:74:17)
at Chart.buildOrUpdateControllers (core.controller.js:445:37)
at Chart.update (core.controller.js:495:33)
at new Chart (core.controller.js:193:12)
at (index):31:1
```

The same behavior as with `null` can be observed with `dataElementType` set to `false` (allowed by docs) and eve with `dataElementType` not being defined at all (kinda allowed by the unused type, disallowed by docs).

This happens in
[`src/core/core.datasetController.js::DatasetController::_insertElements`](https://github.com/chartjs/Chart.js/blob/master/src/core/core.datasetController.js#L1009) which does not check the value of `dataElementType` in any way. This function also allocates the `meta.data` array even though it should not be needed in this case (especially when parsing disabled - probably?).

I found also something similar in
[`src/core/core.datasetController.js::DatasetController::constructor`](https://github.com/chartjs/Chart.js/blob/master/src/core/core.datasetController.js#L275) but this somehow never gets called, at least in my experiments.

### Reproducible sample

https://jsfiddle.net/cb37sr54/2/

### Optional extra steps/info to reproduce

Gist GitHub: https://gist.github.com/antoninkriz/2ced01a7e90db8ac2dd417064e6a08a7

JSFiddle: https://jsfiddle.net/cb37sr54/2/

This code will raise the described error.

```html

{
"imports": {
"@kurkle/color": "https://cdn.jsdelivr.net/npm/@kurkle/color@0.4.0/+esm",
"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chunks/helpers.dataset.js": "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chunks/helpers.dataset.js"
}
}

import { Chart } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { DatasetController } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { BarController, BarElement, CategoryScale, LinearScale } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'

class MyController extends DatasetController {
static id = 'my';
static defaults = {
dataElementType: false,
}
parse() { }
draw() { }
}

Chart.register(MyController)
Chart.register(BarController, BarElement, CategoryScale, LinearScale);

new Chart(document.getElementById('myChart'), {
data: {
labels: ['a', 'b', 'c', 'd'],
datasets: [{
type: 'my',
data: [1, 2, 3, 4],
}],
},
});

```

### Possible solution

Here I define an element to keep the code from crying and successfully render a completely empty chart.

Anyway since I do not define `inRange` and `events: []` is not set in the config I get error on mouse moves over the chart.

```
core.interaction.js:174 Uncaught TypeError: element.inRange is not a function
at evaluationFunc (core.interaction.js:174:29)
at evaluateInteractionItems (core.interaction.js:83:9)
at getNearestCartesianItems (core.interaction.js:195:3)
at getNearestItems (core.interaction.js:216:7)
at nearest (core.interaction.js:356:14)
at Chart.getElementsAtEventForMode (core.controller.js:823:14)
at Chart._getActiveElements (core.controller.js:1260:17)
at Chart._handleEvent (core.controller.js:1213:25)
at Chart._eventHandler (core.controller.js:1176:26)
at listener (core.controller.js:988:12)
```

```html

{
"imports": {
"@kurkle/color": "https://cdn.jsdelivr.net/npm/@kurkle/color@0.4.0/+esm",
"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chunks/helpers.dataset.js": "https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chunks/helpers.dataset.js"
}
}

import { Chart } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { DatasetController, Element } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'
import { BarController, BarElement, CategoryScale, LinearScale } from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.0/chart.js'

class NoopElement extends Element {
static id = 'noop'
}

class MyController extends DatasetController {
static id = 'my';
static defaults = {
dataElementType: 'noop',
}
parse() { }
draw() { }
}

Chart.register(NoopElement, MyController)
Chart.register(BarController, BarElement, CategoryScale, LinearScale);

new Chart(document.getElementById('myChart'), {
data: {
labels: ['a', 'b', 'c', 'd'],
datasets: [{
type: 'my',
data: [1, 2, 3, 4],
}],
},
});

```

### Context

_No response_

### chart.js version

4.5.0

### Browser name and version

Chrome 143, macOS 26, Apple ARM

### Link to your project

_No response_

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.