Skip to content

Commit 456e46b

Browse files
authored
Add getAnnotations function to plugin and remove _getState private one (#892)
* add getAnnotation function * add function for coverage just for interaction * add test case on getannotations * add doc
1 parent 87de801 commit 456e46b

File tree

18 files changed

+151
-64
lines changed

18 files changed

+151
-64
lines changed

docs/.vuepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export default defineConfig({
134134
'types/polygon'
135135
]
136136
},
137+
'developers',
137138
{
138139
title: 'Migration',
139140
collapsable: true,

docs/guide/developers.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Developers
2+
3+
## Access to the annotation elements
4+
5+
The annotation plugin uses Chart.js elements to draw the annotation requested by the user. The following APIs allows the user to get the created annotation elements to use in callbacks or for other purposes.
6+
The APIs are available in the annotation plugin instance.
7+
8+
#### Script Tag
9+
10+
```html
11+
<script>
12+
// get annotation plugin instance
13+
const annotationPlugin = window['chartjs-plugin-annotation'];
14+
</script>
15+
```
16+
17+
#### Bundlers (Webpack, Rollup, etc.)
18+
19+
```javascript
20+
// get annotation plugin instance
21+
import annotationPlugin from 'chartjs-plugin-annotation';
22+
```
23+
24+
### `.getAnnotations(chart: Chart): AnnotationElement[]`
25+
26+
It provides all annotation elements configured by the plugin options, even if the annotations are not visible.
27+
28+
```javascript
29+
const myLineChart = new Chart(ctx, config);
30+
// get all annotation elements
31+
const elements = annotationPlugin.getAnnotations(myLineChart);
32+
```
33+

docs/guide/integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ title: Integration
1010
<script src="path/to/chartjs/dist/chart.umd.js"></script>
1111
<script src="path/to/chartjs-plugin-annotation/dist/chartjs-plugin-annotation.min.js"></script>
1212
<script>
13-
var myChart = new Chart(ctx, {...});
13+
const myChart = new Chart(ctx, {...});
1414
</script>
1515
```
1616

src/annotation.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {handleEvent, eventHooks, updateListeners} from './events';
44
import {invokeHook, elementHooks, updateHooks} from './hooks';
55
import {adjustScaleRange, verifyScaleOptions} from './scale';
66
import {updateElements, resolveType, isIndexable} from './elements';
7+
import {getElements} from './interaction';
78
import {annotationTypes} from './types';
89
import {requireVersion} from './helpers';
910
import {version} from '../package.json';
@@ -101,8 +102,14 @@ export default {
101102
chartStates.delete(chart);
102103
},
103104

104-
_getState(chart) {
105-
return chartStates.get(chart);
105+
getAnnotations(chart) {
106+
const state = chartStates.get(chart);
107+
return state ? state.elements : [];
108+
},
109+
110+
// only for testing
111+
_getAnnotationElementsAtEventForMode(visibleElements, event, options) {
112+
return getElements(visibleElements, event, options);
106113
},
107114

108115
defaults: {

src/events.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export const eventHooks = moveHooks.concat('click');
1919
export function updateListeners(chart, state, options) {
2020
state.listened = loadHooks(options, eventHooks, state.listeners);
2121
state.moveListened = false;
22-
state._getElements = getElements; // for testing
2322

2423
moveHooks.forEach(hook => {
2524
if (isFunction(options[hook])) {
@@ -71,7 +70,7 @@ function handleMoveEvents(state, event, options) {
7170
let elements;
7271

7372
if (event.type === 'mousemove') {
74-
elements = getElements(state, event, options.interaction);
73+
elements = getElements(state.visibleElements, event, options.interaction);
7574
} else {
7675
elements = [];
7776
}
@@ -96,7 +95,7 @@ function dispatchMoveEvents({state, event}, hook, elements, checkElements) {
9695

9796
function handleClickEvents(state, event, options) {
9897
const listeners = state.listeners;
99-
const elements = getElements(state, event, options.interaction);
98+
const elements = getElements(state.visibleElements, event, options.interaction);
10099
let changed;
101100
for (const element of elements) {
102101
changed = dispatchEvent(element.options.click || listeners.click, element, event) || changed;

src/interaction.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,58 @@ const interaction = {
99
modes: {
1010
/**
1111
* Point mode returns all elements that hit test based on the event position
12-
* @param {Object} state - the state of the plugin
12+
* @param {AnnotationElement[]} visibleElements - annotation elements which are visible
1313
* @param {ChartEvent} event - the event we are find things at
1414
* @return {AnnotationElement[]} - elements that are found
1515
*/
16-
point(state, event) {
17-
return filterElements(state, event, {intersect: true});
16+
point(visibleElements, event) {
17+
return filterElements(visibleElements, event, {intersect: true});
1818
},
1919

2020
/**
2121
* Nearest mode returns the element closest to the event position
22-
* @param {Object} state - the state of the plugin
22+
* @param {AnnotationElement[]} visibleElements - annotation elements which are visible
2323
* @param {ChartEvent} event - the event we are find things at
2424
* @param {Object} options - interaction options to use
2525
* @return {AnnotationElement[]} - elements that are found (only 1 element)
2626
*/
27-
nearest(state, event, options) {
28-
return getNearestItem(state, event, options);
27+
nearest(visibleElements, event, options) {
28+
return getNearestItem(visibleElements, event, options);
2929
},
3030
/**
3131
* x mode returns the elements that hit-test at the current x coordinate
32-
* @param {Object} state - the state of the plugin
32+
* @param {AnnotationElement[]} visibleElements - annotation elements which are visible
3333
* @param {ChartEvent} event - the event we are find things at
3434
* @param {Object} options - interaction options to use
3535
* @return {AnnotationElement[]} - elements that are found
3636
*/
37-
x(state, event, options) {
38-
return filterElements(state, event, {intersect: options.intersect, axis: 'x'});
37+
x(visibleElements, event, options) {
38+
return filterElements(visibleElements, event, {intersect: options.intersect, axis: 'x'});
3939
},
4040

4141
/**
4242
* y mode returns the elements that hit-test at the current y coordinate
43-
* @param {Object} state - the state of the plugin
43+
* @param {AnnotationElement[]} visibleElements - annotation elements which are visible
4444
* @param {ChartEvent} event - the event we are find things at
4545
* @param {Object} options - interaction options to use
4646
* @return {AnnotationElement[]} - elements that are found
4747
*/
48-
y(state, event, options) {
49-
return filterElements(state, event, {intersect: options.intersect, axis: 'y'});
48+
y(visibleElements, event, options) {
49+
return filterElements(visibleElements, event, {intersect: options.intersect, axis: 'y'});
5050
}
5151
}
5252
};
5353

5454
/**
5555
* Returns all elements that hit test based on the event position
56-
* @param {Object} state - the state of the plugin
56+
* @param {AnnotationElement[]} visibleElements - annotation elements which are visible
5757
* @param {ChartEvent} event - the event we are find things at
5858
* @param {Object} options - interaction options to use
5959
* @return {AnnotationElement[]} - elements that are found
6060
*/
61-
export function getElements(state, event, options) {
61+
export function getElements(visibleElements, event, options) {
6262
const mode = interaction.modes[options.mode] || interaction.modes.nearest;
63-
return mode(state, event, options);
63+
return mode(visibleElements, event, options);
6464
}
6565

6666
function inRangeByAxis(element, event, axis) {
@@ -79,14 +79,14 @@ function getPointByAxis(event, center, axis) {
7979
return center;
8080
}
8181

82-
function filterElements(state, event, options) {
83-
return state.visibleElements.filter((element) => options.intersect ? element.inRange(event.x, event.y) : inRangeByAxis(element, event, options.axis));
82+
function filterElements(visibleElements, event, options) {
83+
return visibleElements.filter((element) => options.intersect ? element.inRange(event.x, event.y) : inRangeByAxis(element, event, options.axis));
8484
}
8585

86-
function getNearestItem(state, event, options) {
86+
function getNearestItem(visibleElements, event, options) {
8787
let minDistance = Number.POSITIVE_INFINITY;
8888

89-
return filterElements(state, event, options)
89+
return filterElements(visibleElements, event, options)
9090
.reduce((nearestItems, element) => {
9191
const center = element.getCenterPoint();
9292
const evenPoint = getPointByAxis(event, center, options.axis);

test/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {acquireChart, addMatchers, releaseCharts, specsFromFixtures, triggerMouseEvent, afterEvent} from 'chartjs-test-utils';
22
import {testEvents, eventPoint0, getCenterPoint} from './events';
3-
import {createCanvas, getAnnotationElements, scatterChart, stringifyObject, interactionData, getQuadraticXY, getQuadraticAngle, drawStar} from './utils';
3+
import {createCanvas, getAnnotationElements, getAnnotationInteractedElements, scatterChart, stringifyObject, interactionData, getQuadraticXY, getQuadraticAngle, drawStar} from './utils';
44
import * as helpers from '../src/helpers';
55

66
window.helpers = helpers;
@@ -14,6 +14,7 @@ window.getCenterPoint = getCenterPoint;
1414
window.createCanvas = createCanvas;
1515
window.drawStar = drawStar;
1616
window.getAnnotationElements = getAnnotationElements;
17+
window.getAnnotationInteractedElements = getAnnotationInteractedElements;
1718
window.scatterChart = scatterChart;
1819
window.stringifyObject = stringifyObject;
1920
window.interactionData = interactionData;

test/integration/ts/basic.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ const chart = new Chart('id', {
3232
},
3333
plugins: [Annotation]
3434
});
35+
36+
const elements = Annotation.getAnnotations(chart);

test/specs/annotation.spec.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,34 @@ describe('Annotation plugin', function() {
142142
console.warn = origWarn;
143143
});
144144

145+
it('should return the right amount of annotations elements', function() {
146+
const types = ['box', 'ellipse', 'label', 'line', 'point', 'polygon'];
147+
const annotations = types.map(function(type) {
148+
return {
149+
type,
150+
display: () => type.startsWith('l'),
151+
xMin: 2,
152+
yMin: 2,
153+
xMax: 8,
154+
yMax: 8
155+
};
156+
});
157+
158+
const chart = acquireChart({
159+
type: 'line',
160+
options: {
161+
plugins: {
162+
annotation: {
163+
annotations
164+
}
165+
}
166+
}
167+
});
168+
169+
expect(window.getAnnotationElements(chart).length).toBe(types.length);
170+
expect(window.getAnnotationElements(undefined).length).toBe(0);
171+
});
172+
145173
describe('Annotation option resolution', function() {
146174
it('should resolve from plugin common options', function() {
147175
const chart = acquireChart({

test/specs/box.spec.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,11 @@ describe('Box annotation', function() {
153153
};
154154

155155
const chart = window.scatterChart(10, 10, {outer, inner});
156-
const state = window['chartjs-plugin-annotation']._getState(chart);
156+
const elements = window.getAnnotationElements(chart);
157+
const visible = elements.filter(el => !el.skip && el.options.display);
157158
const interactionOpts = {};
158-
const outerEl = window.getAnnotationElements(chart)[0];
159-
const innerEl = window.getAnnotationElements(chart)[1];
159+
const outerEl = elements[0];
160+
const innerEl = elements[1];
160161

161162
it('should return the right amount of annotation elements', function() {
162163
for (const interaction of window.interactionData) {
@@ -177,8 +178,8 @@ describe('Box annotation', function() {
177178
for (let i = 0; i < points.length; i++) {
178179
const point = points[i];
179180
const elementsCount = elementsCounts[i];
180-
const elements = state._getElements(state, point, interactionOpts);
181-
expect(elements.length).withContext(`with interaction mode ${mode}, axis ${axis}, intersect ${intersect}, {x: ${point.x.toFixed(1)}, y: ${point.y.toFixed(1)}}`).toEqual(elementsCount);
181+
const els = window.getAnnotationInteractedElements(visible, point, interactionOpts);
182+
expect(els.length).withContext(`with interaction mode ${mode}, axis ${axis}, intersect ${intersect}, {x: ${point.x.toFixed(1)}, y: ${point.y.toFixed(1)}}`).toEqual(elementsCount);
182183
}
183184
});
184185
}

0 commit comments

Comments
 (0)