Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(D3 plugin): add chart click event #430

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/plugins/d3/__stories__/area/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react';

import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';

import {D3Plugin} from '../..';
import {ChartKit} from '../../../../components/ChartKit';
import {settings} from '../../../../libs';
import {ChartKitWidgetData} from '../../../../types';
import {HighchartsPlugin} from '../../../highcharts';

function prepareData(): ChartKitWidgetData {
return {
series: {
options: {
line: {
lineWidth: 2,
},
},
data: [
{
name: 'A',
type: 'area',
data: [
{x: 1, y: 200},
{x: 2, y: 220},
{x: 3, y: 180},
],
stacking: 'normal',
dataLabels: {
enabled: true,
},
},
{
name: 'B',
type: 'area',
data: [
{x: 1, y: 30},
{x: 2, y: 25},
{x: 3, y: 45},
],
stacking: 'normal',
dataLabels: {
enabled: true,
},
},
],
},
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

const ChartStory = ({data}: {data: ChartKitWidgetData}) => {
const [shown, setShown] = React.useState(false);

if (!shown) {
settings.set({plugins: [D3Plugin, HighchartsPlugin]});
return <Button onClick={() => setShown(true)}>Show chart</Button>;
}

return (
<>
<div
style={{
height: '80vh',
width: '100%',
}}
>
<ChartKit type="d3" data={data} />
</div>
</>
);
};

export const PlaygroundLineChartStory: StoryObj<typeof ChartStory> = {
name: 'Playground',
args: {
data: prepareData(),
},
argTypes: {
data: {
control: 'object',
},
},
};

export default {
title: 'Plugins/D3/Area',
component: ChartStory,
};
7 changes: 6 additions & 1 deletion src/plugins/d3/__stories__/bar-x/Playground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function prepareData(): ChartKitWidgetData {
},
xAxis: {
type: 'category',
categories: gamesByPlatform.map(([key]) => key),
categories: gamesByPlatform.map(([key, _group]) => key),
title: {
text: 'Game Platforms',
},
Expand All @@ -48,6 +48,11 @@ function prepareData(): ChartKitWidgetData {
},
},
],
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/plugins/d3/__stories__/bar-y/Playground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ function prepareData(): ChartKitWidgetData {
},
},
],
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/plugins/d3/__stories__/line/Playground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ function prepareData(): ChartKitWidgetData {
},
},
],
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
5 changes: 5 additions & 0 deletions src/plugins/d3/__stories__/pie/Playground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ function prepareData(): ChartKitWidgetData {
],
},
legend: {enabled: true},
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
89 changes: 89 additions & 0 deletions src/plugins/d3/__stories__/scatter/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';

import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';

import {D3Plugin} from '../..';
import {ChartKit} from '../../../../components/ChartKit';
import {settings} from '../../../../libs';
import {ChartKitWidgetData} from '../../../../types';
import nintendoGames from '../../examples/nintendoGames';

function prepareData() {
const dataset = nintendoGames.filter((d) => d.date && d.user_score);
const data = dataset.map((d) => ({
x: d.date || undefined,
y: d.user_score || undefined,
custom: d,
}));

const widgetData: ChartKitWidgetData = {
series: {
data: [
{
type: 'scatter',
data,
name: 'Scatter series',
},
],
},
yAxis: [
{
title: {
text: 'User score',
},
},
],
xAxis: {
type: 'datetime',
title: {
text: 'Release dates',
},
},
chart: {
events: {
click: action('chart.events.click'),
},
},
};

return widgetData;
}

const ChartStory = ({data}: {data: ChartKitWidgetData}) => {
const [shown, setShown] = React.useState(false);

if (!shown) {
settings.set({plugins: [D3Plugin]});
return <Button onClick={() => setShown(true)}>Show chart</Button>;
}

return (
<div
style={{
height: '80vh',
width: '100%',
}}
>
<ChartKit type="d3" data={data} />
</div>
);
};

export const PlaygroundBarYChartStory: StoryObj<typeof ChartStory> = {
name: 'Playground',
args: {
data: prepareData(),
},
argTypes: {
data: {
control: 'object',
},
},
};

export default {
title: 'Plugins/D3/Scatter',
component: ChartStory,
};
51 changes: 28 additions & 23 deletions src/plugins/d3/__stories__/treemap/Playground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,36 @@ import type {ChartKitWidgetData} from '../../../../types/widget-data';
import {D3Plugin} from '../..';

const prepareData = (): ChartKitWidgetData => {
const treemapSeries: TreemapSeries = {
type: 'treemap',
name: 'Example',
dataLabels: {
enabled: true,
},
layoutAlgorithm: 'binary',
levels: [{index: 1}, {index: 2}, {index: 3}],
data: [
{name: 'One', value: 15},
{name: 'Two', value: 10},
{name: 'Three', value: 15},
{name: 'Four'},
{name: 'Four-1', value: 5, parentId: 'Four'},
{name: 'Four-2', parentId: 'Four'},
{name: 'Four-3', value: 4, parentId: 'Four'},
{name: 'Four-2-1', value: 5, parentId: 'Four-2'},
{name: 'Four-2-2', value: 7, parentId: 'Four-2'},
{name: 'Four-2-3', value: 10, parentId: 'Four-2'},
],
};

return {
series: {
data: [
{
type: 'treemap',
name: 'Example',
dataLabels: {
enabled: true,
},
layoutAlgorithm: 'binary',
levels: [{index: 1}, {index: 2}, {index: 3}],
data: [
{name: 'One', value: 15},
{name: 'Two', value: 10},
{name: 'Three', value: 15},
{name: 'Four'},
{name: 'Four-1', value: 5, parentId: 'Four'},
{name: 'Four-2', parentId: 'Four'},
{name: 'Four-3', value: 4, parentId: 'Four'},
{name: 'Four-2-1', value: 5, parentId: 'Four-2'},
{name: 'Four-2-2', value: 7, parentId: 'Four-2'},
{name: 'Four-2-3', value: 10, parentId: 'Four-2'},
],
},
],
data: [treemapSeries],
},
chart: {
events: {
click: action('chart.events.click'),
},
},
};
};
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/d3/renderer/components/Chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ export const Chart = (props: Props) => {
yScale,
svgContainer: svgRef.current,
});
React.useEffect(() => {
if (data.chart?.events?.click) {
dispatcher.on('click-chart', data.chart?.events?.click);
}

return () => {
dispatcher.on('click-chart', null);
};
}, [dispatcher]);

const boundsOffsetTop = chart.margin.top;
const boundsOffsetLeft = chart.margin.left + getWidthOccupiedByYAxis({preparedAxis: yAxis});
Expand Down
30 changes: 26 additions & 4 deletions src/plugins/d3/renderer/components/Tooltip/TooltipTriggerArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export const TooltipTriggerArea = (args: Args) => {
return sort(result, (item) => item.y);
}, [shapesData]);

const handleMouseMove: React.MouseEventHandler<SVGRectElement> = (e) => {
const getShapeData = (point: [number, number]) => {
const {left: ownLeft, top: ownTop} = rectRef.current?.getBoundingClientRect() || {
left: 0,
top: 0,
Expand All @@ -178,10 +178,10 @@ export const TooltipTriggerArea = (args: Args) => {
left: 0,
top: 0,
};
const [pointerX, pointerY] = pointer(e, svgContainer);
const hoverShapeData = [];
const [pointerX, pointerY] = point; //pointer(e, svgContainer);
const result = [];

hoverShapeData?.push(
result?.push(
...getBarXShapeData({
shapesData,
point: [pointerX, pointerY],
Expand All @@ -200,6 +200,13 @@ export const TooltipTriggerArea = (args: Args) => {
}),
);

return result;
};

const handleMouseMove: React.MouseEventHandler<SVGRectElement> = (e) => {
const [pointerX, pointerY] = pointer(e, svgContainer);
const hoverShapeData = getShapeData([pointerX, pointerY]);

if (hoverShapeData.length) {
const position: PointerPosition = [pointerX, pointerY];
dispatcher.call('hover-shape', e.target, hoverShapeData, position);
Expand All @@ -213,6 +220,20 @@ export const TooltipTriggerArea = (args: Args) => {
dispatcher.call('hover-shape', {}, undefined);
};

const handleClick: React.MouseEventHandler<SVGRectElement> = (e) => {
const [pointerX, pointerY] = pointer(e, svgContainer);
const shapeData = getShapeData([pointerX, pointerY]);

if (shapeData.length) {
dispatcher.call(
'click-chart',
undefined,
{point: get(shapeData, '[0].data'), series: get(shapeData, '[0].series')},
e,
);
}
};

return (
<rect
ref={rectRef}
Expand All @@ -221,6 +242,7 @@ export const TooltipTriggerArea = (args: Args) => {
fill="transparent"
onMouseMove={throttledHandleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={handleClick}
/>
);
};
2 changes: 1 addition & 1 deletion src/plugins/d3/renderer/d3-dispatcher.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {dispatch} from 'd3';

export const getD3Dispatcher = () => {
return dispatch('hover-shape');
return dispatch('hover-shape', 'click-chart');
};
Loading
Loading