From b6dbb969e7e5c0758ca1634981c26711b722083d Mon Sep 17 00:00:00 2001 From: MiniPear Date: Tue, 30 May 2023 17:54:09 +0800 Subject: [PATCH] feat(interaction): emit legend highlight --- .../api-chart-emit-legend-highlight.spec.ts | 71 +++++++++++++++++ .../api/chart-emit-legend-highlight/step0.png | Bin 0 -> 12324 bytes .../api/chart-emit-legend-highlight/step1.png | Bin 0 -> 11769 bytes __tests__/integration/utils/event.ts | 5 ++ .../plots/api/chart-emit-legend-highlight.ts | 72 ++++++++++++++++++ __tests__/plots/api/index.ts | 1 + .../spec/interaction/legendHighlight.zh.md | 28 +++++++ src/interaction/legendHighlight.ts | 69 +++++++++++++---- 8 files changed, 232 insertions(+), 14 deletions(-) create mode 100644 __tests__/integration/api-chart-emit-legend-highlight.spec.ts create mode 100644 __tests__/integration/snapshots/api/chart-emit-legend-highlight/step0.png create mode 100644 __tests__/integration/snapshots/api/chart-emit-legend-highlight/step1.png create mode 100644 __tests__/plots/api/chart-emit-legend-highlight.ts diff --git a/__tests__/integration/api-chart-emit-legend-highlight.spec.ts b/__tests__/integration/api-chart-emit-legend-highlight.spec.ts new file mode 100644 index 0000000000..52ea2205c3 --- /dev/null +++ b/__tests__/integration/api-chart-emit-legend-highlight.spec.ts @@ -0,0 +1,71 @@ +import { chartEmitLegendHighlight as render } from '../plots/api/chart-emit-legend-highlight'; +import { + LEGEND_ITEMS_CLASS_NAME, + CATEGORY_LEGEND_CLASS_NAME, +} from '../../src/interaction/legendFilter'; +import { createNodeGCanvas } from './utils/createNodeGCanvas'; +import { sleep } from './utils/sleep'; +import { kebabCase } from './utils/kebabCase'; +import { createPromise, dispatchFirstShapeEvent } from './utils/event'; +import './utils/useSnapshotMatchers'; +import './utils/useCustomFetch'; + +describe('chart.emit', () => { + const dir = `${__dirname}/snapshots/api/${kebabCase(render.name)}`; + const canvas = createNodeGCanvas(800, 500); + + it('chart.on("legend:highlight") should receive expected data.', async () => { + const { chart, finished } = render({ + canvas, + container: document.createElement('div'), + }); + await finished; + await sleep(20); + + // chart.emit('legend:highlight', options) should trigger slider. + chart.emit('legend:highlight', { + data: { channel: 'color', value: 'Increase' }, + }); + await sleep(20); + await expect(canvas).toMatchCanvasSnapshot(dir, 'step0'); + + // chart.emit('legend:unhighlight', options) should reset. + chart.emit('legend:unhighlight', {}); + await sleep(20); + await expect(canvas).toMatchCanvasSnapshot(dir, 'step1'); + + chart.off(); + + // chart.on("legend:unhighlight") should be called. + const [unhighlight, resolveUnhighlight] = createPromise(); + chart.on('legend:unhighlight', (event) => { + if (!event.nativeEvent) return; + resolveUnhighlight(); + }); + dispatchFirstShapeEvent( + canvas, + CATEGORY_LEGEND_CLASS_NAME, + 'pointerleave', + { nativeEvent: true }, + ); + await sleep(20); + await unhighlight; + + // chart.on("legend:highlight") should receive expected data. + const [highlight, resolveHighlight] = createPromise(); + chart.on('legend:highlight', (event) => { + if (!event.nativeEvent) return; + expect(event.data).toEqual({ channel: 'color', value: 'Increase' }); + resolveHighlight(); + }); + dispatchFirstShapeEvent(canvas, LEGEND_ITEMS_CLASS_NAME, 'pointerover', { + nativeEvent: true, + }); + await sleep(20); + await highlight; + }); + + afterAll(() => { + canvas?.destroy(); + }); +}); diff --git a/__tests__/integration/snapshots/api/chart-emit-legend-highlight/step0.png b/__tests__/integration/snapshots/api/chart-emit-legend-highlight/step0.png new file mode 100644 index 0000000000000000000000000000000000000000..6e0482bf88f094f02896341746adf0ba33cc4451 GIT binary patch literal 12324 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 Z>^&th6l|_51zr0Gj1A26%Pu+J{~z-+l}i8s literal 0 HcmV?d00001 diff --git a/__tests__/integration/snapshots/api/chart-emit-legend-highlight/step1.png b/__tests__/integration/snapshots/api/chart-emit-legend-highlight/step1.png new file mode 100644 index 0000000000000000000000000000000000000000..2e7090046192efe7fb778bd7513da7d4611b4d1f GIT binary patch literal 11769 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 Lb7YyG)BXPjjT0dn literal 0 HcmV?d00001 diff --git a/__tests__/integration/utils/event.ts b/__tests__/integration/utils/event.ts index d2cd94d972..33bc925fdc 100644 --- a/__tests__/integration/utils/event.ts +++ b/__tests__/integration/utils/event.ts @@ -39,3 +39,8 @@ export function dispatchPlotEvent(canvas, event, params?) { const [plot] = canvas.document.getElementsByClassName('plot'); plot.dispatchEvent(new CustomEvent(event, params)); } + +export function dispatchFirstShapeEvent(canvas, className, event, params?) { + const [shape] = canvas.document.getElementsByClassName(className); + shape.dispatchEvent(new CustomEvent(event, params)); +} diff --git a/__tests__/plots/api/chart-emit-legend-highlight.ts b/__tests__/plots/api/chart-emit-legend-highlight.ts new file mode 100644 index 0000000000..adb6dcaba8 --- /dev/null +++ b/__tests__/plots/api/chart-emit-legend-highlight.ts @@ -0,0 +1,72 @@ +import { Chart } from '../../../src'; +import { profit } from '../../data/profit'; + +export function chartEmitLegendHighlight(context) { + const { container, canvas } = context; + + // button + const button = document.createElement('button'); + button.innerText = 'highlight'; + container.appendChild(button); + + const button1 = document.createElement('button'); + button1.innerText = 'reset'; + container.appendChild(button1); + + // wrapperDiv + const wrapperDiv = document.createElement('div'); + container.appendChild(wrapperDiv); + + const chart = new Chart({ + theme: 'classic', + container: wrapperDiv, + canvas, + }); + + chart.options({ + paddingLeft: 60, + type: 'interval', + data: profit, + axis: { y: { labelFormatter: '~s' } }, + encode: { + x: 'month', + y: ['end', 'start'], + color: (d) => + d.month === 'Total' ? 'Total' : d.profit > 0 ? 'Increase' : 'Decrease', + }, + state: { inactive: { opacity: 0.5 } }, + legend: { + color: { state: { inactive: { labelOpacity: 0.5, markerOpacity: 0.5 } } }, + }, + interaction: { + legendHighlight: true, + tooltip: false, + }, + }); + + chart.on('legend:highlight', (e) => { + const { nativeEvent, data } = e; + if (!nativeEvent) return; + console.log(data); + }); + + chart.on('legend:unhighlight', (e) => { + const { nativeEvent } = e; + if (!nativeEvent) return; + console.log('unhighlight'); + }); + + button.onclick = () => { + chart.emit('legend:highlight', { + data: { channel: 'color', value: 'Increase' }, + }); + }; + + button1.onclick = () => { + chart.emit('legend:unhighlight', {}); + }; + + const finished = chart.render(); + + return { chart, finished }; +} diff --git a/__tests__/plots/api/index.ts b/__tests__/plots/api/index.ts index 2084a0ee08..3ec42d1be1 100644 --- a/__tests__/plots/api/index.ts +++ b/__tests__/plots/api/index.ts @@ -29,3 +29,4 @@ export { chartEmitSliderFilter } from './chart-emit-slider-filter'; export { chartEmitElementHighlight } from './chart-emit-element-highlight'; export { chartEmitElementSelect } from './chart-emit-element-select'; export { chartEmitElementSelectSingle } from './chart-emit-element-select-single'; +export { chartEmitLegendHighlight } from './chart-emit-legend-highlight'; diff --git a/site/docs/spec/interaction/legendHighlight.zh.md b/site/docs/spec/interaction/legendHighlight.zh.md index 9a813c081c..c09c70fc90 100644 --- a/site/docs/spec/interaction/legendHighlight.zh.md +++ b/site/docs/spec/interaction/legendHighlight.zh.md @@ -35,3 +35,31 @@ chart.interaction('legendHighlight', true); chart.render(); ``` + +## 案例 + +### 获得数据 + +```js +chart.on('legend:highlight', (e) => { + const { nativeEvent, data } = e; + if (!nativeEvent) return; + console.log(data); +}); + +chart.on('legend:unhighlight', (e) => { + const { nativeEvent } = e; + if (!nativeEvent) return; + console.log('unhighlight'); +}); +``` + +### 触发交互 + +```js +chart.emit('legend:highlight', { + data: { channel: 'color', value: 'Increase' }, +}); + +chart.emit('legend:unhighlight', {}); +``` diff --git a/src/interaction/legendHighlight.ts b/src/interaction/legendHighlight.ts index e88efa5fc2..e322ef6e39 100644 --- a/src/interaction/legendHighlight.ts +++ b/src/interaction/legendHighlight.ts @@ -10,7 +10,7 @@ import { import { markerOf, labelOf, itemsOf, legendsOf, dataOf } from './legendFilter'; export function LegendHighlight() { - return (context) => { + return (context, _, emitter) => { const { container, view, options } = context; const legends = legendsOf(container); const elements = selectG2Elements(container); @@ -25,6 +25,7 @@ export function LegendHighlight() { }; const markState = mergeState(options, ['active', 'inactive']); const valueof = createValueof(elements, createDatumof(view)); + const destroys = []; // Bind events for each legend. for (const legend of legends) { @@ -62,39 +63,79 @@ export function LegendHighlight() { } } }; + const highlightItem = (event, item) => { + // Update UI. + const value = datumOf(item); + const elementSet = new Set(elementGroup.get(value)); + for (const e of elements) { + if (elementSet.has(e)) setState(e, 'active'); + else setState(e, 'inactive'); + } + updateLegendState(item); + + // Emit events. + const { nativeEvent = true } = event; + if (!nativeEvent) return; + emitter.emit('legend:highlight', { + ...event, + nativeEvent, + data: { channel, value }, + }); + }; const itemPointerover = new Map(); // Add listener for the legend items. for (const item of items) { - const pointerover = () => { - const value = datumOf(item); - const elementSet = new Set(elementGroup.get(value)); - for (const e of elements) { - if (elementSet.has(e)) setState(e, 'active'); - else setState(e, 'inactive'); - } - updateLegendState(item); + const pointerover = (event) => { + highlightItem(event, item); }; item.addEventListener('pointerover', pointerover); itemPointerover.set(item, pointerover); } // Add listener for the legend group. - const pointerleave = () => { - for (const e of elements) { - removeState(e, 'inactive', 'active'); - } + const pointerleave = (event) => { + for (const e of elements) removeState(e, 'inactive', 'active'); updateLegendState(null); + + // Emit events. + const { nativeEvent = true } = event; + if (!nativeEvent) return; + emitter.emit('legend:unhighlight', { nativeEvent }); + }; + + const onHighlight = (event) => { + const { nativeEvent, data } = event; + if (nativeEvent) return; + const { channel: specifiedChannel, value } = data; + if (specifiedChannel !== channel) return; + const item = items.find((d) => datumOf(d) === value); + if (!item) return; + highlightItem({ nativeEvent: false }, item); }; + + const onUnHighlight = (event) => { + const { nativeEvent } = event; + if (nativeEvent) return; + pointerleave({ nativeEvent: false }); + }; + legend.addEventListener('pointerleave', pointerleave); + emitter.on('legend:highlight', onHighlight); + emitter.on('legend:unhighlight', onUnHighlight); - return () => { + const destroy = () => { legend.removeEventListener(pointerleave); + emitter.off('legend:highlight', onHighlight); + emitter.off('legend:unhighlight', onUnHighlight); for (const [item, pointerover] of itemPointerover) { item.removeEventListener(pointerover); } }; + destroys.push(destroy); } + + return () => destroys.forEach((d) => d()); }; }