Skip to content

Commit ab5f111

Browse files
committed
[ChartJs] Adding a better way to register Chartjs plugins
1 parent c6e3560 commit ab5f111

File tree

4 files changed

+108
-8
lines changed

4 files changed

+108
-8
lines changed

src/Chartjs/assets/dist/controller.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
import { Controller } from '@hotwired/stimulus';
22
import Chart from 'chart.js/auto';
33

4+
let isChartInitialized = false;
45
class default_1 extends Controller {
56
constructor() {
67
super(...arguments);
78
this.chart = null;
89
}
910
connect() {
11+
if (!isChartInitialized) {
12+
isChartInitialized = true;
13+
this.dispatchEvent('init', {
14+
Chart,
15+
});
16+
}
1017
if (!(this.element instanceof HTMLCanvasElement)) {
1118
throw new Error('Invalid element');
1219
}
1320
const payload = this.viewValue;
1421
if (Array.isArray(payload.options) && 0 === payload.options.length) {
1522
payload.options = {};
1623
}
17-
this.dispatchEvent('pre-connect', { options: payload.options });
24+
this.dispatchEvent('pre-connect', {
25+
options: payload.options,
26+
config: payload,
27+
});
1828
const canvasContext = this.element.getContext('2d');
1929
if (!canvasContext) {
2030
throw new Error('Could not getContext() from Element');

src/Chartjs/assets/src/controller.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import { Controller } from '@hotwired/stimulus';
1313
import Chart from 'chart.js/auto';
1414

15+
let isChartInitialized = false;
16+
1517
export default class extends Controller {
1618
declare readonly viewValue: any;
1719

@@ -22,6 +24,13 @@ export default class extends Controller {
2224
private chart: Chart | null = null;
2325

2426
connect() {
27+
if (!isChartInitialized) {
28+
isChartInitialized = true;
29+
this.dispatchEvent('init', {
30+
Chart,
31+
});
32+
}
33+
2534
if (!(this.element instanceof HTMLCanvasElement)) {
2635
throw new Error('Invalid element');
2736
}
@@ -31,7 +40,10 @@ export default class extends Controller {
3140
payload.options = {};
3241
}
3342

34-
this.dispatchEvent('pre-connect', { options: payload.options });
43+
this.dispatchEvent('pre-connect', {
44+
options: payload.options,
45+
config: payload,
46+
});
3547

3648
const canvasContext = this.element.getContext('2d');
3749
if (!canvasContext) {

src/Chartjs/assets/test/controller.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@ import { Application } from '@hotwired/stimulus';
1313
import { waitFor } from '@testing-library/dom';
1414
import ChartjsController from '../src/controller';
1515

16+
// Kept track of globally, but just used in one test.
17+
// This is because, by the time that test has run, it is likely that the
18+
// chartjs:init event has already been dispatched. So, we capture it out here.
19+
let initCallCount = 0;
20+
1621
const startChartTest = async (canvasHtml: string): Promise<{ canvas: HTMLCanvasElement, chart: Chart }> => {
1722
let chart: Chart | null = null;
1823

24+
document.body.addEventListener('chartjs:init', () => {
25+
initCallCount++;
26+
});
27+
1928
document.body.addEventListener('chartjs:pre-connect', () => {
2029
document.body.classList.add('pre-connected');
2130
});
@@ -95,4 +104,51 @@ describe('ChartjsController', () => {
95104
expect(chart.options.showLines).toBe(true);
96105
});
97106
});
107+
108+
it('dispatches the events correctly', async () => {
109+
let preConnectCallCount = 0;
110+
let preConnectDetail: any = null;
111+
112+
document.body.addEventListener('chartjs:pre-connect', (event: any) => {
113+
preConnectCallCount++;
114+
preConnectDetail = event.detail;
115+
});
116+
117+
await startChartTest(`
118+
<canvas
119+
data-testid="canvas"
120+
data-controller="chartjs"
121+
data-chartjs-view-value="&#x7B;&quot;type&quot;&#x3A;&quot;line&quot;,&quot;data&quot;&#x3A;&#x7B;&quot;labels&quot;&#x3A;&#x5B;&quot;January&quot;,&quot;February&quot;,&quot;March&quot;,&quot;April&quot;,&quot;May&quot;,&quot;June&quot;,&quot;July&quot;&#x5D;,&quot;datasets&quot;&#x3A;&#x5B;&#x7B;&quot;label&quot;&#x3A;&quot;My&#x20;First&#x20;dataset&quot;,&quot;backgroundColor&quot;&#x3A;&quot;rgb&#x28;255,&#x20;99,&#x20;132&#x29;&quot;,&quot;borderColor&quot;&#x3A;&quot;rgb&#x28;255,&#x20;99,&#x20;132&#x29;&quot;,&quot;data&quot;&#x3A;&#x5B;0,10,5,2,20,30,45&#x5D;&#x7D;&#x5D;&#x7D;,&quot;options&quot;&#x3A;&#x7B;&quot;showLines&quot;&#x3A;false&#x7D;&#x7D;"
122+
></canvas>
123+
`);
124+
expect(initCallCount).toBe(1);
125+
expect(preConnectCallCount).toBe(1);
126+
expect(preConnectDetail.options.showLines).toBe(false);
127+
expect(preConnectDetail.config.type).toBe('line');
128+
expect(preConnectDetail.config.data.datasets[0].data).toEqual([0, 10, 5, 2, 20, 30, 45]);
129+
130+
// add a second chart!
131+
const canvas = document.createElement('canvas');
132+
canvas.dataset.controller = 'chartjs';
133+
canvas.dataset.chartjsViewValue = JSON.stringify({
134+
type: 'line',
135+
data: {
136+
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
137+
datasets: [{
138+
label: 'My First dataset',
139+
backgroundColor: 'rgb(255, 99, 132)',
140+
borderColor: 'rgb(255, 99, 132)',
141+
data: [0, 10, 5, 2, 20, 30, 45],
142+
}],
143+
},
144+
options: {
145+
showLines: false,
146+
},
147+
});
148+
document.body.appendChild(canvas);
149+
150+
await waitFor(() => expect(preConnectCallCount).toBe(2));
151+
// still only initialized once
152+
expect(initCallCount).toBe(1);
153+
});
98154
});

src/Chartjs/doc/index.rst

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,20 @@ First, install the plugin:
9898
$ npm install chartjs-plugin-zoom -D
9999
100100
# or use yarn
101-
$ yarn add chartjs-plugin-zoom -dev
101+
$ yarn add chartjs-plugin-zoom --dev
102102
103103
Then register the plugin globally. This can be done in your ``app.js`` file:
104104

105105
.. code-block:: javascript
106106
107107
// assets/app.js
108-
109-
import { Chart } from 'chart.js';
110108
import zoomPlugin from 'chartjs-plugin-zoom';
111109
112-
Chart.register(zoomPlugin);
110+
// register globally for all charts
111+
document.addEventListener('chartjs:init', function (event) {
112+
const Chart = event.detail.Chart;
113+
Chart.register(zoomPlugin);
114+
});
113115
114116
// ...
115117
@@ -177,10 +179,11 @@ custom Stimulus controller:
177179
178180
_onPreConnect(event) {
179181
// The chart is not yet created
180-
console.log(event.detail.options); // You can access the chart options using the event details
182+
// You can access the config that will be passed to "new Chart()"
183+
console.log(event.detail.config);
181184
182185
// For instance you can format Y axis
183-
event.detail.options.scales = {
186+
event.detail.config.options.scales = {
184187
yAxes: [
185188
{
186189
ticks: {
@@ -213,6 +216,24 @@ Then in your render call, add your controller as an HTML attribute:
213216
214217
{{ render_chart(chart, {'data-controller': 'mychart'}) }}
215218
219+
There is also a ``chartjs:init`` event that is called just *one* time before your
220+
first chart is rendered. That's an ideal place to `register plugins globally <Using Plugins>`_
221+
or make other changes to any "static"/global part of Chart.js. For example,
222+
to add a global `Tooltip positioner`_:
223+
224+
.. code-block:: javascript
225+
226+
// assets/app.js
227+
228+
// register globally for all charts
229+
document.addEventListener('chartjs:init', function (event) {
230+
const Chart = event.detail.Chart;
231+
const Tooltip = Chart.registry.plugins.get('tooltip');
232+
Tooltip.positioners.bottom = function(items) {
233+
/* ... */
234+
};
235+
});
236+
216237
Backward Compatibility promise
217238
------------------------------
218239

@@ -228,3 +249,4 @@ the Symfony framework: https://symfony.com/doc/current/contributing/code/bc.html
228249
.. _`a lot of plugins`: https://github.com/chartjs/awesome#plugins
229250
.. _`zoom plugin`: https://www.chartjs.org/chartjs-plugin-zoom/latest/
230251
.. _`zoom plugin documentation`: https://www.chartjs.org/chartjs-plugin-zoom/latest/guide/integration.html
252+
.. _`Tooltip positioner`: https://www.chartjs.org/docs/latest/samples/tooltip/position.html

0 commit comments

Comments
 (0)