Skip to content

Commit

Permalink
feat(D3 plugin): add chart click event
Browse files Browse the repository at this point in the history
  • Loading branch information
kuzmadom committed Feb 13, 2024
1 parent 0dfb616 commit e79f033
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 38 deletions.
94 changes: 94 additions & 0 deletions src/plugins/d3/__stories__/area/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';
import React from 'react';

import {settings} from '../../../../libs';
import {D3Plugin} from '../..';
import {ChartKitWidgetData} from '../../../../types';
import {ChartKit} from '../../../../components/ChartKit';
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,
};
13 changes: 10 additions & 3 deletions src/plugins/d3/__stories__/bar-x/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import {StoryObj} from '@storybook/react';
import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';
import {groups} from 'd3';
import React from 'react';

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

function prepareData(): ChartKitWidgetData {
Expand Down Expand Up @@ -48,6 +50,11 @@ function prepareData(): ChartKitWidgetData {
},
},
],
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
13 changes: 10 additions & 3 deletions src/plugins/d3/__stories__/bar-y/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import {StoryObj} from '@storybook/react';
import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';
import {groups} from 'd3';
import React from 'react';

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

function prepareData(): ChartKitWidgetData {
Expand Down Expand Up @@ -49,6 +51,11 @@ function prepareData(): ChartKitWidgetData {
},
},
],
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
11 changes: 9 additions & 2 deletions src/plugins/d3/__stories__/line/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import {StoryObj} from '@storybook/react';
import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';
import React from 'react';

import {settings} from '../../../../libs';
import {D3Plugin} from '../..';
import {ChartKitWidgetData, LineSeriesData} from '../../../../types';
Expand Down Expand Up @@ -84,6 +86,11 @@ function prepareData(): ChartKitWidgetData {
},
},
],
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
13 changes: 10 additions & 3 deletions src/plugins/d3/__stories__/pie/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React from 'react';
import {StoryObj} from '@storybook/react';
import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';
import {groups, sort, descending} from 'd3';
import React from 'react';

import {settings} from '../../../../libs';
import {D3Plugin} from '../..';
import {ChartKitWidgetData} from '../../../../types';
import {ChartKit} from '../../../../components/ChartKit';
import nintendoGames from '../../examples/nintendoGames';
import {HighchartsPlugin} from '../../../highcharts';
import {groups, sort, descending} from 'd3';

function prepareData(): ChartKitWidgetData {
const gamesByPlatform = groups(nintendoGames, (item) => item.platform);
Expand All @@ -31,6 +33,11 @@ function prepareData(): ChartKitWidgetData {
],
},
legend: {enabled: true},
chart: {
events: {
click: action('chart.events.click'),
},
},
};
}

Expand Down
14 changes: 10 additions & 4 deletions src/plugins/d3/__stories__/treemap/Playground.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';
import {StoryObj} from '@storybook/react';
import {Button} from '@gravity-ui/uikit';
import {action} from '@storybook/addon-actions';
import {StoryObj} from '@storybook/react';
import React from 'react';

import {settings} from '../../../../libs';
import {ChartKit} from '../../../../components/ChartKit';
import type {ChartKitRef} from '../../../../types';
import type {ChartKitWidgetData} from '../../../../types/widget-data';
import type {ChartKitRef, ChartKitWidgetData} from '../../../../types';
import {D3Plugin} from '../..';

const prepareData = (): ChartKitWidgetData => {
Expand Down Expand Up @@ -34,6 +35,11 @@ const prepareData = (): ChartKitWidgetData => {
},
],
},
chart: {
events: {
click: action('chart.events.click'),
},
},
};
};

Expand Down
7 changes: 6 additions & 1 deletion src/plugins/d3/renderer/components/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useEffect} from 'react';

import type {ChartKitWidgetData} from '../../../../types';
import {block} from '../../../../utils/cn';
Expand Down Expand Up @@ -37,6 +37,11 @@ export const Chart = (props: Props) => {
const dispatcher = React.useMemo(() => {
return getD3Dispatcher();
}, []);
useEffect(() => {
if (data.chart?.events?.click) {
dispatcher.on('click-chart', data.chart?.events?.click);
}
}, [dispatcher]);
const {chart, title, tooltip} = useChartOptions({
data,
});
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');
};
34 changes: 22 additions & 12 deletions src/plugins/d3/renderer/hooks/useShapes/pie/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,23 +143,22 @@ export function PieSeriesShapes(args: PreparePieSeriesArgs) {
}
});

const getSelectedSegment = (element: Element) => {
const datum = select<BaseType, PieArcDatum<SegmentData> | PieLabelData>(
element,
).datum();
const seriesId = get(datum, 'data.series.id', get(datum, 'series.id'));
return preparedData.reduce<SegmentData | undefined>((result, pie) => {
return result || pie.segments.find((s) => s.data.series.id === seriesId)?.data;
}, undefined);
};

const eventName = `hover-shape.pie`;
const hoverOptions = get(seriesOptions, 'pie.states.hover');
const inactiveOptions = get(seriesOptions, 'pie.states.inactive');
svgElement
.on('mousemove', (e) => {
const datum = select<BaseType, PieArcDatum<SegmentData> | PieLabelData>(
e.target,
).datum();
const seriesId = get(datum, 'data.series.id', get(datum, 'series.id'));
const currentSegment = preparedData.reduce<SegmentData | undefined>(
(result, pie) => {
return (
result || pie.segments.find((s) => s.data.series.id === seriesId)?.data
);
},
undefined,
);
const currentSegment = getSelectedSegment(e.target);

if (currentSegment) {
const data: TooltipDataChunkPie = {
Expand All @@ -176,6 +175,17 @@ export function PieSeriesShapes(args: PreparePieSeriesArgs) {
})
.on('mouseleave', () => {
dispatcher.call('hover-shape', {}, undefined);
})
.on('click', (e) => {
const selectedSegment = getSelectedSegment(e.target);
if (selectedSegment) {
dispatcher.call(
'click-chart',
undefined,
{point: selectedSegment.series.data, series: selectedSegment.series},
e,
);
}
});

dispatcher.on(eventName, (data?: TooltipDataChunkPie[]) => {
Expand Down
Loading

0 comments on commit e79f033

Please sign in to comment.