diff --git a/__tests__/integration/api-chart-on-label-click.spec.ts b/__tests__/integration/api-chart-on-label-click.spec.ts new file mode 100644 index 0000000000..a7ed756fb0 --- /dev/null +++ b/__tests__/integration/api-chart-on-label-click.spec.ts @@ -0,0 +1,27 @@ +import { chartOnLabelClick as render } from '../plots/api/chart-on-label-click'; +import { ChartEvent } from '../../src'; +import { createNodeGCanvas } from './utils/createNodeGCanvas'; +import { dispatchFirstShapeEvent, createPromise } from './utils/event'; +import './utils/useSnapshotMatchers'; + +describe('chart.on', () => { + const canvas = createNodeGCanvas(640, 480); + const { finished, chart } = render({ canvas }); + + chart.off(); + + it('chart.on("label:click", callback) should provide datum for item element', async () => { + await finished; + const [fired, resolve] = createPromise(); + chart.on(`label:${ChartEvent.CLICK}`, (event) => { + expect(event.data).toEqual({ data: { text: 'A', value: 12000, c: 's' } }); + resolve(); + }); + dispatchFirstShapeEvent(canvas, 'label', 'click', { detail: 1 }); + await fired; + }); + + afterAll(() => { + canvas?.destroy(); + }); +}); diff --git a/__tests__/plots/api/chart-on-label-click.ts b/__tests__/plots/api/chart-on-label-click.ts new file mode 100644 index 0000000000..af5ff82c84 --- /dev/null +++ b/__tests__/plots/api/chart-on-label-click.ts @@ -0,0 +1,28 @@ +import { Chart } from '../../../src'; + +export function chartOnLabelClick(context) { + const { container, canvas } = context; + const data = [ + { text: 'A', value: 12000, c: 's' }, + { text: 'B', value: 9800 }, + { text: 'C', value: 6789 }, + { text: 'D', value: 4569 }, + ]; + const chart = new Chart({ container, canvas }); + chart + .interval() + .data(data) + .encode('x', 'text') + .encode('y', 'value') + .legend(false) + .label({ + text: 'label', + cursor: 'pointer', + }); + + chart.on('label:click', (e) => console.log('click label', e, e.data)); + + const finished = chart.render(); + + return { chart, finished }; +} diff --git a/__tests__/plots/api/index.ts b/__tests__/plots/api/index.ts index 04c71a7d57..362aefa220 100644 --- a/__tests__/plots/api/index.ts +++ b/__tests__/plots/api/index.ts @@ -48,3 +48,4 @@ export { chartOptionsCallbackChildren } from './chart-options-callback-children' export { chartAutoFitSlider } from './chart-auto-fit-slider'; export { chart3d } from './chart-3d'; export { chartOnScrollbarFilter } from './chart-on-scrollbar-filter'; +export { chartOnLabelClick } from './chart-on-label-click'; diff --git a/site/docs/manual/extra-topics/event.en.md b/site/docs/manual/extra-topics/event.en.md index 24f273cb95..2cbac9faf0 100644 --- a/site/docs/manual/extra-topics/event.en.md +++ b/site/docs/manual/extra-topics/event.en.md @@ -112,6 +112,12 @@ chart.on('plot:click', (event) => console.log(event)); chart.on('component:click', (event) => console.log(event)); ``` +* Listen to global label events + +```js +chart.on('label:click', (event) => console.log(event)); +``` + ### Click event | Event name | Explanation | Callback parameters | diff --git a/site/docs/manual/extra-topics/event.zh.md b/site/docs/manual/extra-topics/event.zh.md index d141c001bb..156477e571 100644 --- a/site/docs/manual/extra-topics/event.zh.md +++ b/site/docs/manual/extra-topics/event.zh.md @@ -113,6 +113,12 @@ chart.on('plot:click', (event) => console.log(event)); chart.on('component:click', (event) => console.log(event)); ``` +- 监听全局 label 事件 + +```js +chart.on('label:click', (event) => console.log(event)); +``` + ### 点击事件 | 事件名 | 说明 | 回调参数 | diff --git a/src/interaction/event.ts b/src/interaction/event.ts index c183df5784..6fbf2a4b97 100644 --- a/src/interaction/event.ts +++ b/src/interaction/event.ts @@ -31,6 +31,11 @@ function maybeElementRoot(node) { return maybeRoot(node, (node) => node.className === 'element'); } +// For extended label. +function maybeLabelRoot(node) { + return maybeRoot(node, (node) => node.className === 'label'); +} + function bubblesEvent(eventType, view, emitter, predicate = (event) => true) { return (e) => { if (!predicate(e)) return; @@ -52,19 +57,27 @@ function bubblesEvent(eventType, view, emitter, predicate = (event) => true) { const elementRoot = maybeElementRoot(target); // If target is component or child of component. const componentRoot = maybeComponentRoot(target); - const root = elementRoot || componentRoot; + // If target is babel or child of babel. + const babelRoot = maybeLabelRoot(target); + + const root = elementRoot || componentRoot || babelRoot; + if (!root) return; const { className: elementType, markType } = root; + const e1 = { + ...e, + nativeEvent: true, + }; if (elementType === 'element') { - const e1 = { - ...e, - nativeEvent: true, - data: { data: dataOf(root, view) }, - }; + e1['data'] = { data: dataOf(root, view) }; emitter.emit(`element:${eventType}`, e1); emitter.emit(`${markType}:${eventType}`, e1); + } else if (elementType === 'label') { + //label children [Text2, Rect2, Path2], + e1['data'] = { data: root.attributes.datum }; + emitter.emit(`label:${eventType}`, e1); + emitter.emit(`${className}:${eventType}`, e1); } else { - const e1 = { ...e, nativeEvent: true }; emitter.emit(`component:${eventType}`, e1); emitter.emit(`${className}:${eventType}`, e1); }