Skip to content

Commit

Permalink
feat(element): handle click event (close: #4665)
Browse files Browse the repository at this point in the history
  • Loading branch information
pearmini committed Feb 28, 2023
1 parent d83adcd commit cae2c13
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 5 deletions.
38 changes: 38 additions & 0 deletions __tests__/integration/chart-on-item-element.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { CustomEvent } from '@antv/g';
import { chartOnItemElement as render } from '../plots/api/chart-on-item-element';
import { createDOMGCanvas } from './utils/createDOMGCanvas';
import { sleep } from './utils/sleep';
import './utils/useSnapshotMatchers';

describe('chart.on', () => {
const canvas = createDOMGCanvas(640, 480);

it('chart.on("element:click", callback) should provide datum for item element', async () => {
const { finished, chart } = render({ canvas });
await finished;
await sleep(20);

await new Promise<void>((resolve) => {
let count = 0;
const click = (event) => {
count++;
expect(event.dataset.data).toEqual({
genre: 'Sports',
sold: 275,
});
if (count >= 2) resolve();
};

chart.off();
chart.on('element:click', click);
chart.on('interval:click', click);

const [element] = canvas.document.getElementsByClassName('element');
element.dispatchEvent(new CustomEvent('click'));
});
});

afterAll(() => {
canvas?.destroy();
});
});
98 changes: 98 additions & 0 deletions __tests__/integration/chart-on-series-element.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { CustomEvent } from '@antv/g';
import { chartOnSeriesElement as render } from '../plots/api/chart-on-series-element';
import { createDOMGCanvas } from './utils/createDOMGCanvas';
import { sleep } from './utils/sleep';
import './utils/useSnapshotMatchers';

const data = [
{
month: 'Jan',
city: 'Tokyo',
temperature: 7,
},
{
month: 'Feb',
city: 'Tokyo',
temperature: 6.9,
},
{
month: 'Mar',
city: 'Tokyo',
temperature: 9.5,
},
{
month: 'Apr',
city: 'Tokyo',
temperature: 14.5,
},
{
month: 'May',
city: 'Tokyo',
temperature: 18.4,
},
{
month: 'Jun',
city: 'Tokyo',
temperature: 21.5,
},
{
month: 'Jul',
city: 'Tokyo',
temperature: 25.2,
},
{
month: 'Aug',
city: 'Tokyo',
temperature: 26.5,
},
{
month: 'Sep',
city: 'Tokyo',
temperature: 23.3,
},
{
month: 'Oct',
city: 'Tokyo',
temperature: 18.3,
},
{
month: 'Nov',
city: 'Tokyo',
temperature: 13.9,
},
{
month: 'Dec',
city: 'Tokyo',
temperature: 9.6,
},
];

describe('chart.on', () => {
const canvas = createDOMGCanvas(640, 480);

it('chart.on("element:click", callback) should provide data for series element', async () => {
const { finished, chart } = render({ canvas });
await finished;
await sleep(20);

await new Promise<void>((resolve) => {
let count = 0;
const click = (event) => {
count++;
expect(event.dataset.data).toEqual(data);
if (count >= 2) resolve();
};

chart.off();
chart.on('element:click', click);
chart.on('line:click', click);

const [element] = canvas.document.getElementsByClassName('element');
element.dispatchEvent(new CustomEvent('click'));
});
});

afterAll(() => {
canvas?.destroy();
});
});
37 changes: 37 additions & 0 deletions __tests__/plots/api/chart-on-item-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Chart } from '../../../src';

export function chartOnItemElement(context) {
const { container, canvas } = context;

const chart = new Chart({
container,
canvas,
});

chart.data([
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 },
]);

chart
.interval()
.encode('x', 'genre')
.encode('y', 'sold')
.encode('color', 'genre')
.axis({ x: { animate: false }, y: { animate: false } });

chart.on('interval:click', (e) => {
console.log(e.dataset.data);
});

chart.on('element:click', (e) => {
console.log(e.dataset.data);
});

const finished = chart.render();

return { chart, finished };
}
32 changes: 32 additions & 0 deletions __tests__/plots/api/chart-on-series-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Chart } from '../../../src';
import { temperatures } from '../../data/temperatures';

export function chartOnSeriesElement(context) {
const { container, canvas } = context;

const chart = new Chart({
container,
canvas,
});

chart.data(temperatures);

chart
.line()
.encode('x', 'month')
.encode('y', 'temperature')
.encode('color', 'city')
.style('strokeWidth', 10);

chart.on('line:click', (e) => {
console.log(e.dataset.data);
});

chart.on('element:click', (e) => {
console.log(e.dataset.data);
});

const finished = chart.render();

return { chart, finished };
}
2 changes: 2 additions & 0 deletions __tests__/plots/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export { chartChangeSize } from './chart-change-size';
export { chartAutoFit } from './chart-auto-fit';
export { markChangeData } from './mark-change-data';
export { markChangeDataTooltip } from './mark-change-data-tooltip';
export { chartOnItemElement } from './chart-on-item-element';
export { chartOnSeriesElement } from './chart-on-series-element';
1 change: 1 addition & 0 deletions src/interaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { BrushXFilter } from './native/brushXFilter';
export { BrushYFilter } from './native/brushYFilter';
export { SliderFilter } from './native/sliderFilter';
export { Poptip } from './native/poptip';
export { Event } from './native/event';

export { LEGEND_ITEMS_CLASS_NAME } from './native/legendFilter';

Expand Down
45 changes: 45 additions & 0 deletions src/interaction/native/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
function dataOf(element, view) {
const { __data__: datum } = element;
const { markKey, index, seriesIndex } = datum;
const { markState } = view;
const selectedMark: any = Array.from(markState.keys()).find(
(mark) => (mark as any).key === markKey,
);
if (!selectedMark) return;
if (seriesIndex) {
return seriesIndex.map((i) => selectedMark.data[i]);
}
return selectedMark.data[index];
}

function clickEvent(view, emitter) {
return (e) => {
const { target } = e;
const { classList } = target;
const [elementType, markType] = classList;
if (elementType === 'element') {
const data = dataOf(target, view);
const { dataset = {} } = e;
dataset.data = data;
e.dataset = dataset;
emitter.emit('element:click', e);
emitter.emit(`${markType}:click`, e);
return;
}
// @todo Handle click axis and legend.
emitter.emit(`${elementType}:click`);
};
}

// @todo More events such as mouseenter, mouseleave.
// @todo Provide more info for event.dataset.
export function Event() {
return (context, _, emitter) => {
const { container, view } = context;
const click = clickEvent(view, emitter);
container.addEventListener('click', click);
return () => {
container.removeEventListener('click', click);
};
};
}
14 changes: 9 additions & 5 deletions src/runtime/plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export async function plot<T extends G2ViewTree>(
// Apply interactions.
for (const option of inferInteraction(options)) {
const interaction = useInteraction(option);
const destroy = interaction(target, enterViewInstances);
const destroy = interaction(target, enterViewInstances, context.emitter);
nameInteraction.set(option.type, { destroy });
}
}
Expand All @@ -248,7 +248,7 @@ export async function plot<T extends G2ViewTree>(

// Apply new interaction.
const interaction = useInteraction(option);
const destroy = interaction(target, updateViewInstances);
const destroy = interaction(target, updateViewInstances, context.emitter);
nameInteraction.set(options.type, { destroy });
}
}
Expand Down Expand Up @@ -743,12 +743,14 @@ async function plotView(
// Render marks with corresponding data.
for (const [mark, state] of markState.entries()) {
const { data } = state;
const { key, class: cls } = mark;
const { key, class: cls, type } = mark;
const shapeFunction = createMarkShapeFunction(mark, state, view, library);
const enterFunction = createEnterFunction(mark, state, view, library);
const updateFunction = createUpdateFunction(mark, state, view, library);
const exitFunction = createExitFunction(mark, state, view, library);
const facetElements = selectFacetElements(selection, cls, 'element');
const classNames = [ELEMENT_CLASS_NAME];
if (typeof type === 'string') classNames.push(type);
const T = selection
.select(`#${key}`)
.selectAll(className(ELEMENT_CLASS_NAME))
Expand All @@ -762,7 +764,7 @@ async function plotView(
(enter) =>
enter
.append(shapeFunction)
.attr('className', ELEMENT_CLASS_NAME)
.attr('className', classNames.join(' '))
.transition(function (data) {
return enterFunction(data, [this]);
}),
Expand Down Expand Up @@ -1294,7 +1296,9 @@ function inferTheme(theme: G2ThemeOptions = { type: 'light' }): G2ThemeOptions {
* @todo Infer builtin tooltips.
*/
function inferInteraction(view: G2View): G2InteractionOptions[] {
const defaults = {};
const defaults = {
event: true,
};
const { interaction = {} } = view;
return Object.entries(deepMix(defaults, interaction))
.filter((d) => !!d[1])
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/types/component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Coordinate, Transformation } from '@antv/coord';
import { DisplayObject, IAnimation as GAnimation } from '@antv/g';
import EventEmitter from '@antv/event-emitter';
import { G2ViewTree } from './options';
import {
G2Theme,
Expand Down Expand Up @@ -191,6 +192,7 @@ export type AnimationComponent<O = Record<string, unknown>> = G2BaseComponent<
export type Interaction = (
target: G2ViewInstance,
viewInstances: G2ViewInstance[],
emitter?: EventEmitter,
) => void;
export type InteractionComponent<O = Record<string, unknown>> = G2BaseComponent<
Interaction,
Expand Down
2 changes: 2 additions & 0 deletions src/stdlib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ import {
BrushYFilter,
SliderFilter,
Poptip,
Event,
// ElementHighlightByX,
// ElementHighlightByColor,
// ElementListHighlight,
Expand Down Expand Up @@ -521,6 +522,7 @@ export function createLibrary(): G2Library {
'interaction.brushYFilter': BrushYFilter,
'interaction.sliderFilter': SliderFilter,
'interaction.poptip': Poptip,
'interaction.event': Event,
// 'interaction.elementSelected': ElementSelected,
// 'interaction.elementHighlight': ElementHighlight,
// 'interaction.elementHighlightByX': ElementHighlightByX,
Expand Down

0 comments on commit cae2c13

Please sign in to comment.