diff --git a/packages/radar/src/Radar.tsx b/packages/radar/src/Radar.tsx index dd47b47400..82c4112b0e 100644 --- a/packages/radar/src/Radar.tsx +++ b/packages/radar/src/Radar.tsx @@ -53,6 +53,7 @@ const InnerRadar = >({ ariaDescribedBy, defs = svgDefaultProps.defs, fill = svgDefaultProps.fill, + onClick, }: InnerRadarProps) => { const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions( width, @@ -154,6 +155,7 @@ const InnerRadar = >({ rotation={rotation} angleStep={angleStep} tooltip={sliceTooltip} + onClick={onClick} /> ) diff --git a/packages/radar/src/RadarLayer.tsx b/packages/radar/src/RadarLayer.tsx index e6b1acf23d..d01499268e 100644 --- a/packages/radar/src/RadarLayer.tsx +++ b/packages/radar/src/RadarLayer.tsx @@ -4,7 +4,7 @@ import { lineRadial, CurveFactory } from 'd3-shape' import { ScaleLinear } from 'd3-scale' import { useMotionConfig, useTheme, useAnimatedPath } from '@nivo/core' import { useInheritedColor } from '@nivo/colors' -import { RadarCommonProps } from './types' +import { RadarCommonProps, RadarSvgProps } from './types' interface RadarLayerProps> { data: D[] @@ -19,6 +19,7 @@ interface RadarLayerProps> { borderColor: RadarCommonProps['borderColor'] fillOpacity: RadarCommonProps['fillOpacity'] blendMode: RadarCommonProps['blendMode'] + onClick?: RadarSvgProps['onClick'] } export const RadarLayer = >({ diff --git a/packages/radar/src/RadarSlice.tsx b/packages/radar/src/RadarSlice.tsx index 19a3c6ffd4..d42d148258 100644 --- a/packages/radar/src/RadarSlice.tsx +++ b/packages/radar/src/RadarSlice.tsx @@ -2,7 +2,7 @@ import { useMemo, useState, useCallback, createElement, MouseEvent } from 'react import { Arc } from 'd3-shape' import { positionFromAngle, useTheme } from '@nivo/core' import { useTooltip } from '@nivo/tooltip' -import { RadarCommonProps, RadarDataProps, RadarSliceTooltipDatum } from './types' +import { RadarCommonProps, RadarDataProps, RadarSliceTooltipDatum, RadarSvgProps } from './types' interface RadarSliceProps> { datum: D @@ -15,6 +15,7 @@ interface RadarSliceProps> { radius: number arcGenerator: Arc tooltip: RadarCommonProps['sliceTooltip'] + onClick?: RadarSvgProps['onClick'] } export const RadarSlice = >({ @@ -28,11 +29,17 @@ export const RadarSlice = >({ endAngle, arcGenerator, tooltip, + onClick, }: RadarSliceProps) => { const [isHover, setIsHover] = useState(false) const theme = useTheme() const { showTooltipFromEvent, hideTooltip } = useTooltip() + const handleClick = useCallback( + (event: MouseEvent) => onClick?.(datum, event), + [onClick, datum] + ) + const tooltipData = useMemo(() => { const data: RadarSliceTooltipDatum[] = keys.map(key => ({ color: colorByKey[key], @@ -88,6 +95,7 @@ export const RadarSlice = >({ onMouseEnter={showItemTooltip} onMouseMove={showItemTooltip} onMouseLeave={hideItemTooltip} + onClick={handleClick} /> ) diff --git a/packages/radar/src/RadarSlices.tsx b/packages/radar/src/RadarSlices.tsx index 888d8375d1..074ac0ee7c 100644 --- a/packages/radar/src/RadarSlices.tsx +++ b/packages/radar/src/RadarSlices.tsx @@ -1,6 +1,6 @@ import { arc as d3Arc } from 'd3-shape' import { RadarSlice } from './RadarSlice' -import { RadarColorMapping, RadarCommonProps, RadarDataProps } from './types' +import { RadarColorMapping, RadarCommonProps, RadarDataProps, RadarSvgProps } from './types' interface RadarSlicesProps> { data: RadarDataProps['data'] @@ -12,6 +12,7 @@ interface RadarSlicesProps> { rotation: number angleStep: number tooltip: RadarCommonProps['sliceTooltip'] + onClick?: RadarSvgProps['onClick'] } export const RadarSlices = >({ @@ -24,6 +25,7 @@ export const RadarSlices = >({ rotation, angleStep, tooltip, + onClick, }: RadarSlicesProps) => { const arc = d3Arc<{ startAngle: number; endAngle: number }>().outerRadius(radius).innerRadius(0) @@ -52,6 +54,7 @@ export const RadarSlices = >({ radius={radius} arcGenerator={arc} tooltip={tooltip} + onClick={onClick} /> ) })} diff --git a/packages/radar/src/types.ts b/packages/radar/src/types.ts index 4205593b0e..f8a47ace83 100644 --- a/packages/radar/src/types.ts +++ b/packages/radar/src/types.ts @@ -144,6 +144,16 @@ export type RadarSvgProps> = Partial & Dimensions & MotionProps & - SvgDefsAndFill> + SvgDefsAndFill> & + RadarHandlers export type BoundLegendProps = Required> & Omit + +export type MouseEventHandler = ( + datum: RawDatum, + event: React.MouseEvent +) => void + +export type RadarHandlers = { + onClick?: MouseEventHandler +} diff --git a/packages/radar/tests/Radar.test.tsx b/packages/radar/tests/Radar.test.tsx index 9f47ea2f6a..b7e9b76066 100644 --- a/packages/radar/tests/Radar.test.tsx +++ b/packages/radar/tests/Radar.test.tsx @@ -2,6 +2,8 @@ import { mount } from 'enzyme' import { LegendProps, BoxLegendSvg } from '@nivo/legends' // @ts-ignore import { Radar, RadarSvgProps, RadarSliceTooltipProps } from '../src' +import { act, create } from 'react-test-renderer' +import { RadarSlice } from '../src/RadarSlice' type TestDatum = { A: number @@ -222,3 +224,19 @@ describe('legend', () => { expect(wrapper.find(BoxLegendSvg).find('text').at(3).text()).toBe(customLabels[1].B) }) }) + +describe('interactivity', () => { + it('should support onClick handler', async () => { + const onClick = jest.fn() + const instance = create( {...baseProps} onClick={onClick} />).root + await act(() => { + instance.findAllByType(RadarSlice)[0].findByType('path').props.onClick() + }) + expect(onClick).toHaveBeenCalledTimes(1) + const [datum] = onClick.mock.calls[0] + expect(datum).toHaveProperty('A') + expect(datum).toHaveProperty('B') + expect(datum).not.toHaveProperty('C') + expect(datum).toHaveProperty('category') + }) +}) diff --git a/website/src/data/components/radar/props.ts b/website/src/data/components/radar/props.ts index 02ec532c70..a0550697a1 100644 --- a/website/src/data/components/radar/props.ts +++ b/website/src/data/components/radar/props.ts @@ -387,6 +387,14 @@ const props: ChartProperty[] = [ help: 'Override default slice tooltip.', flavors: ['svg'], }, + { + key: 'onClick', + flavors: ['svg', 'canvas'], + group: 'Interactivity', + help: 'onClick handler, it receives target node data and mouse event.', + type: '(node, event) => void', + required: false, + }, ...motionProperties(['svg'], svgDefaultProps), ] diff --git a/website/src/pages/radar/index.tsx b/website/src/pages/radar/index.tsx index c592cf46a7..809b69dcb8 100644 --- a/website/src/pages/radar/index.tsx +++ b/website/src/pages/radar/index.tsx @@ -114,13 +114,23 @@ const Radar = () => { getTabData={data => data.data} image={image} > - {(properties, data, theme) => { + {(properties, data, theme, logAction) => { return ( + logAction({ + type: 'click', + label: `[slice] {${Object.entries(slice) + .map(([key, value]) => `${key}: ${value}`) + .join(', ')}}`, + color: slice.color, + data: slice, + }) + } /> ) }}