diff --git a/packages/network/src/Network.tsx b/packages/network/src/Network.tsx index ba1c3601d3..06d8b915eb 100644 --- a/packages/network/src/Network.tsx +++ b/packages/network/src/Network.tsx @@ -1,12 +1,18 @@ -import { Fragment, ReactNode, useCallback, createElement } from 'react' +import { Fragment, ReactNode, createElement, useMemo } from 'react' import { Container, useDimensions, SvgWrapper } from '@nivo/core' -import { useTooltip } from '@nivo/tooltip' import { svgDefaultProps } from './defaults' import { useNetwork } from './hooks' import { NetworkLinks } from './NetworkLinks' import { NetworkNodes } from './NetworkNodes' import { NetworkNodeAnnotations } from './NetworkNodeAnnotations' -import { InputNode, LayerId, NodeTooltip, NetworkSvgProps, ComputedNode, InputLink } from './types' +import { + InputNode, + LayerId, + NodeTooltip, + NetworkSvgProps, + InputLink, + CustomLayerProps, +} from './types' type InnerNetworkProps = Omit< NetworkSvgProps, @@ -51,7 +57,11 @@ const InnerNetwork = ({ >, isInteractive = svgDefaultProps.isInteractive, + defaultActiveNodeIds = svgDefaultProps.defaultActiveNodeIds, nodeTooltip = svgDefaultProps.nodeTooltip as NodeTooltip, + onMouseEnter, + onMouseMove, + onMouseLeave, onClick, role = svgDefaultProps.role, @@ -65,7 +75,7 @@ const InnerNetwork = ({ partialMargin ) - const { nodes, links, setActiveNodeIds } = useNetwork({ + const { nodes, links, activeNodeIds, setActiveNodeIds } = useNetwork({ center: [innerWidth / 2, innerHeight / 2], nodes: rawNodes, links: rawLinks, @@ -83,23 +93,10 @@ const InnerNetwork = ({ nodeBorderColor, linkThickness, linkColor, + isInteractive, + defaultActiveNodeIds, }) - const { showTooltipFromEvent, hideTooltip } = useTooltip() - - const handleNodeHover = useCallback( - (node: ComputedNode, event) => { - showTooltipFromEvent(createElement(nodeTooltip, { node }), event) - setActiveNodeIds([node.id]) - }, - [showTooltipFromEvent, nodeTooltip, setActiveNodeIds] - ) - - const handleNodeLeave = useCallback(() => { - hideTooltip() - setActiveNodeIds([]) - }, [hideTooltip, setActiveNodeIds]) - const layerById: Record = { links: null, nodes: null, @@ -119,14 +116,17 @@ const InnerNetwork = ({ if (layers.includes('nodes') && nodes !== null) { layerById.nodes = ( - + key="nodes" nodes={nodes} nodeComponent={nodeComponent} - onClick={isInteractive ? onClick : undefined} - onMouseEnter={isInteractive ? handleNodeHover : undefined} - onMouseMove={isInteractive ? handleNodeHover : undefined} - onMouseLeave={isInteractive ? handleNodeLeave : undefined} + onMouseEnter={onMouseEnter} + onMouseMove={onMouseMove} + onMouseLeave={onMouseLeave} + onClick={onClick} + tooltip={nodeTooltip} + setActiveNodeIds={setActiveNodeIds} + isInteractive={isInteractive} /> ) } @@ -141,6 +141,16 @@ const InnerNetwork = ({ ) } + const customLayerProps: CustomLayerProps = useMemo( + () => ({ + nodes: nodes || [], + links: links || [], + activeNodeIds, + setActiveNodeIds, + }), + [nodes, links, activeNodeIds, setActiveNodeIds] + ) + return ( ({ > {layers.map((layer, i) => { if (typeof layer === 'function') { - return ( - - {createElement(layer, { - // ...props, - // innerWidth, - // innerHeight, - nodes: nodes || [], - links: links || [], - })} - - ) + return {createElement(layer, customLayerProps)} } return layerById?.[layer] ?? null diff --git a/packages/network/src/NetworkCanvas.tsx b/packages/network/src/NetworkCanvas.tsx index b33a9ce7cc..c97476bb74 100644 --- a/packages/network/src/NetworkCanvas.tsx +++ b/packages/network/src/NetworkCanvas.tsx @@ -1,4 +1,4 @@ -import { useCallback, useRef, useEffect, createElement, MouseEvent } from 'react' +import { useCallback, useRef, useEffect, createElement, MouseEvent, useMemo } from 'react' import { getDistance, getRelativeCursor, Container, useDimensions, useTheme } from '@nivo/core' import { useTooltip } from '@nivo/tooltip' import { useComputedAnnotations, renderAnnotationsToCanvas } from '@nivo/annotations' @@ -11,6 +11,7 @@ import { NodeTooltip, InputLink, NetworkSvgProps, + CustomLayerProps, } from './types' type InnerNetworkCanvasProps = Omit< @@ -52,6 +53,7 @@ const InnerNetworkCanvas = ({ >, isInteractive = canvasDefaultProps.isInteractive, + defaultActiveNodeIds = canvasDefaultProps.defaultActiveNodeIds, nodeTooltip = canvasDefaultProps.nodeTooltip as NodeTooltip, onClick, }: InnerNetworkCanvasProps) => { @@ -62,7 +64,7 @@ const InnerNetworkCanvas = ({ partialMargin ) - const { nodes, links, setActiveNodeIds } = useNetwork({ + const { nodes, links, activeNodeIds, setActiveNodeIds } = useNetwork({ center: [innerWidth / 2, innerHeight / 2], nodes: rawNodes, links: rawLinks, @@ -80,6 +82,8 @@ const InnerNetworkCanvas = ({ nodeBorderColor, linkThickness, linkColor, + isInteractive, + defaultActiveNodeIds, }) const boundAnnotations = useNodeAnnotations(nodes!, annotations) @@ -87,6 +91,16 @@ const InnerNetworkCanvas = ({ annotations: boundAnnotations, }) + const customLayerProps: CustomLayerProps = useMemo( + () => ({ + nodes: nodes || [], + links: links || [], + activeNodeIds, + setActiveNodeIds, + }), + [nodes, links, activeNodeIds, setActiveNodeIds] + ) + const theme = useTheme() useEffect(() => { @@ -114,11 +128,7 @@ const InnerNetworkCanvas = ({ theme, }) } else if (typeof layer === 'function' && nodes !== null && links !== null) { - layer(ctx, { - // ...props, - nodes, - links, - }) + layer(ctx, customLayerProps) } }) }, [ @@ -135,6 +145,7 @@ const InnerNetworkCanvas = ({ renderNode, renderLink, computedAnnotations, + customLayerProps, ]) const getNodeFromMouseEvent = useCallback( diff --git a/packages/network/src/NetworkNodes.tsx b/packages/network/src/NetworkNodes.tsx index 542f42e3fc..f920e23b85 100644 --- a/packages/network/src/NetworkNodes.tsx +++ b/packages/network/src/NetworkNodes.tsx @@ -1,15 +1,19 @@ -import { createElement, MouseEvent, useMemo } from 'react' +import { createElement, useCallback, useMemo, MouseEvent } from 'react' import { useTransition } from '@react-spring/web' import { useMotionConfig } from '@nivo/core' -import { InputNode, ComputedNode, NodeAnimatedProps, NodeComponent } from './types' +import { useTooltip } from '@nivo/tooltip' +import { InputNode, ComputedNode, NodeAnimatedProps, NetworkSvgProps, InputLink } from './types' -interface NetworkNodesProps { +interface NetworkNodesProps { nodes: ComputedNode[] - nodeComponent: NodeComponent - onClick?: (node: ComputedNode, event: MouseEvent) => void - onMouseEnter?: (node: ComputedNode, event: MouseEvent) => void - onMouseMove?: (node: ComputedNode, event: MouseEvent) => void - onMouseLeave?: (node: ComputedNode, event: MouseEvent) => void + nodeComponent: NonNullable['nodeComponent']> + onMouseEnter: NetworkSvgProps['onMouseEnter'] + onMouseMove: NetworkSvgProps['onMouseMove'] + onMouseLeave: NetworkSvgProps['onMouseLeave'] + onClick: NetworkSvgProps['onClick'] + tooltip: NonNullable['nodeTooltip']> + setActiveNodeIds: (nodeIds: string[]) => void + isInteractive: NonNullable['isInteractive']> } const getEnterTransition = @@ -51,14 +55,17 @@ const getExitTransition = opacity: 0, }) -export const NetworkNodes = ({ +export const NetworkNodes = ({ nodes, nodeComponent, - onClick, onMouseEnter, onMouseMove, onMouseLeave, -}: NetworkNodesProps) => { + onClick, + tooltip, + setActiveNodeIds, + isInteractive, +}: NetworkNodesProps) => { const { animate, config: springConfig } = useMotionConfig() const [enterTransition, regularTransition, exitTransition] = useMemo( @@ -77,6 +84,34 @@ export const NetworkNodes = ({ immediate: !animate, }) + const { showTooltipFromEvent, hideTooltip } = useTooltip() + + const handleMouseEnter = useCallback( + (node: ComputedNode, event: MouseEvent) => { + showTooltipFromEvent(createElement(tooltip, { node }), event) + setActiveNodeIds([node.id]) + onMouseEnter?.(node, event) + }, + [showTooltipFromEvent, tooltip, setActiveNodeIds, onMouseEnter] + ) + + const handleMouseMove = useCallback( + (node: ComputedNode, event: MouseEvent) => { + showTooltipFromEvent(createElement(tooltip, { node }), event) + onMouseMove?.(node, event) + }, + [showTooltipFromEvent, tooltip, onMouseMove] + ) + + const handleMouseLeave = useCallback( + (node: ComputedNode, event: MouseEvent) => { + hideTooltip() + setActiveNodeIds([]) + onMouseLeave?.(node, event) + }, + [hideTooltip, setActiveNodeIds, onMouseLeave] + ) + return ( <> {transition((transitionProps, node) => @@ -84,10 +119,10 @@ export const NetworkNodes = ({ key: node.id, node, animated: transitionProps, - onClick, - onMouseEnter, - onMouseMove, - onMouseLeave, + onMouseEnter: isInteractive ? handleMouseEnter : undefined, + onMouseMove: isInteractive ? handleMouseMove : undefined, + onMouseLeave: isInteractive ? handleMouseLeave : undefined, + onClick: isInteractive ? onClick : undefined, }) )} diff --git a/packages/network/src/defaults.ts b/packages/network/src/defaults.ts index ef0e728aca..65f9aaac44 100644 --- a/packages/network/src/defaults.ts +++ b/packages/network/src/defaults.ts @@ -9,8 +9,6 @@ export const commonDefaultProps: Omit< NetworkCommonProps, | 'margin' | 'theme' - | 'activeLinkThickness' - | 'defaultActiveNodeIds' | 'onClick' | 'renderWrapper' | 'ariaLabel' @@ -39,6 +37,7 @@ export const commonDefaultProps: Omit< linkColor: { from: 'source.color' }, isInteractive: true, + defaultActiveNodeIds: [], nodeTooltip: NetworkNodeTooltip, annotations: [], diff --git a/packages/network/src/hooks.ts b/packages/network/src/hooks.ts index 8da7f078e0..0f80c623ab 100644 --- a/packages/network/src/hooks.ts +++ b/packages/network/src/hooks.ts @@ -165,6 +165,7 @@ export const useNetwork = ['linkThickness'] linkColor?: NetworkCommonProps['linkColor'] isInteractive?: NetworkCommonProps['isInteractive'] + defaultActiveNodeIds?: NetworkCommonProps['defaultActiveNodeIds'] }) => { // we're using `null` instead of empty array so that we can dissociate // initial rendering from updates when using transitions. @@ -239,7 +241,7 @@ export const useNetwork = ([]) + const [activeNodeIds, setActiveNodeIds] = useState(defaultActiveNodeIds) const getNodeStyle = useNodeStyle({ size: nodeSize, @@ -287,6 +289,7 @@ export const useNetwork = { nodes: ComputedNode[] links: ComputedLink[] + activeNodeIds: string[] + setActiveNodeIds: (nodeIds: string[]) => void } export type CustomLayer = FunctionComponent< CustomLayerProps @@ -154,7 +156,6 @@ export type NetworkCommonProps = > linkThickness: DerivedProp, 'color' | 'thickness'>, number> - activeLinkThickness: DerivedProp, 'color' | 'thickness'>, number> linkColor: InheritedColorConfig, 'color' | 'thickness'>> annotations: AnnotationMatcher>[] @@ -181,6 +182,9 @@ export type NetworkSvgProps = Pa nodeComponent?: NodeComponent linkComponent?: LinkComponent linkBlendMode?: CssMixBlendMode + onMouseEnter?: (node: ComputedNode, event: MouseEvent) => void + onMouseMove?: (node: ComputedNode, event: MouseEvent) => void + onMouseLeave?: (node: ComputedNode, event: MouseEvent) => void } export type NetworkCanvasProps = Partial< diff --git a/packages/network/tests/Network.test.tsx b/packages/network/tests/Network.test.tsx index 8b90b16159..cd3ac96c31 100644 --- a/packages/network/tests/Network.test.tsx +++ b/packages/network/tests/Network.test.tsx @@ -1,9 +1,19 @@ import { mount } from 'enzyme' -// @ts-ignore -import { Network, NetworkSvgProps, NetworkInputNode, svgDefaultProps } from '../src' -import { InputNode } from '../dist/types' +import { Globals } from '@react-spring/web' +import { Annotation } from '@nivo/annotations' +import { + Network, + NetworkSvgProps, + InputNode, + InputLink, + svgDefaultProps, + ComputedNode, + LinkProps, + NodeProps, + // @ts-ignore +} from '../src' -const sampleData: NetworkSvgProps['data'] = { +const sampleData: NetworkSvgProps['data'] = { nodes: [{ id: 'A' }, { id: 'B' }, { id: 'C' }, { id: 'D' }, { id: 'E' }], links: [ { source: 'A', target: 'B' }, @@ -14,13 +24,21 @@ const sampleData: NetworkSvgProps['data'] = { ], } -const baseProps: NetworkSvgProps = { +const baseProps: NetworkSvgProps = { width: 600, height: 600, data: sampleData, animate: false, } +beforeAll(() => { + Globals.assign({ skipAnimation: true }) +}) + +afterAll(() => { + Globals.assign({ skipAnimation: false }) +}) + it('should render a basic network chart', () => { const wrapper = mount() @@ -65,7 +83,7 @@ describe('nodes', () => { index, })) const wrapper = mount( - + {...baseProps} data={{ nodes: nodesWithIndex, @@ -81,6 +99,205 @@ describe('nodes', () => { ) }) }) + + it('custom node component', () => { + const CustomNode = ({ node }: NodeProps) => ( + + ) + + const wrapper = mount() + + sampleData.nodes.forEach(node => { + expect(wrapper.find(`g[data-testid='node.${node.id}.custom']`).exists()).toBeTruthy() + }) + }) +}) + +describe('active/inactive nodes', () => { + it('styles depending on nodes status', () => { + const wrapper = mount( + + ) + + sampleData.nodes.forEach(activeNode => { + wrapper.find(`circle[data-testid='node.${activeNode.id}']`).simulate('mouseenter') + + expect( + wrapper.find(`circle[data-testid='node.${activeNode.id}']`).parent().prop('r').get() + ).toEqual(10) + + sampleData.nodes + .filter(node => node.id !== activeNode.id) + .forEach(otherNode => { + expect( + wrapper + .find(`circle[data-testid='node.${otherNode.id}']`) + .parent() + .prop('r') + .get() + ).toEqual(0) + }) + }) + }) + + it('default active node ids', () => { + const activeIds = ['A', 'C'] + const wrapper = mount( + + ) + + sampleData.nodes.forEach(node => { + const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`) + + if (activeIds.includes(node.id)) { + expect(nodeElement.prop('r')).toEqual(10) + } else { + expect(nodeElement.prop('r')).toEqual(0) + } + }) + }) + + it('ignored if non interactive', () => { + const wrapper = mount( + + ) + + sampleData.nodes.forEach(node => { + expect(wrapper.find(`circle[data-testid='node.${node.id}']`).prop('r')).toEqual(5) + }) + }) +}) + +describe('links', () => { + it('static link thickness', () => { + const wrapper = mount() + + sampleData.links.forEach(link => { + expect( + wrapper + .find(`line[data-testid='link.${link.source}.${link.target}']`) + .prop('strokeWidth') + ).toEqual(3) + }) + }) + + it('dynamic link thickness', () => { + const wrapper = mount( 1 + link.index} />) + + sampleData.links.forEach((link, index) => { + expect( + wrapper + .find(`line[data-testid='link.${link.source}.${link.target}']`) + .prop('strokeWidth') + ).toEqual(1 + index) + }) + }) + + it('static link color', () => { + const color = 'rgba(255, 0, 0, 1)' + const wrapper = mount() + + sampleData.links.forEach(link => { + expect( + wrapper + .find(`line[data-testid='link.${link.source}.${link.target}']`) + .prop('stroke') + ).toEqual(color) + }) + }) + + it('dynamic link color', () => { + const wrapper = mount( + `rgba(${link.index * 10}, 0, 0, 1)`} /> + ) + + sampleData.links.forEach((link, index) => { + expect( + wrapper + .find(`line[data-testid='link.${link.source}.${link.target}']`) + .prop('stroke') + ).toEqual(`rgba(${index * 10}, 0, 0, 1)`) + }) + }) + + it('link color from source node color', () => { + const color = 'rgba(125, 255, 125, 1)' + const wrapper = mount( + + ) + + sampleData.links.forEach(link => { + expect( + wrapper + .find(`line[data-testid='link.${link.source}.${link.target}']`) + .prop('stroke') + ).toEqual(color) + }) + }) + + it('link color from target node color', () => { + const color = 'rgba(125, 125, 255, 1)' + const wrapper = mount( + + ) + + sampleData.links.forEach(link => { + expect( + wrapper + .find(`line[data-testid='link.${link.source}.${link.target}']`) + .prop('stroke') + ).toEqual(color) + }) + }) + + it('link blend mode', () => { + const wrapper = mount() + + sampleData.links.forEach(link => { + expect( + wrapper.find(`line[data-testid='link.${link.source}.${link.target}']`).prop('style') + ).toEqual({ mixBlendMode: 'multiply' }) + }) + }) + + it('custom link component', () => { + const CustomLink = ({ link }: LinkProps) => ( + + ) + + const wrapper = mount() + + sampleData.links.forEach(link => { + expect( + wrapper.find(`g[data-testid='link.${link.source}.${link.target}.custom']`).exists() + ).toBeTruthy() + }) + }) }) describe('tooltip', () => { @@ -100,4 +317,210 @@ describe('tooltip', () => { expect(wrapper.find(svgDefaultProps.nodeTooltip).children()).toHaveLength(0) }) }) + + it('disabled if non interactive', () => { + const wrapper = mount() + + sampleData.nodes.forEach(node => { + const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`) + + nodeElement.simulate('mouseenter') + + expect(wrapper.find(svgDefaultProps.nodeTooltip).children()).toHaveLength(0) + }) + }) + + it('custom node tooltip', () => { + const CustomTooltip = ({ node }: { node: ComputedNode }) => ( +
Custom: {node.id}
+ ) + const wrapper = mount() + + sampleData.nodes.forEach(node => { + const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`) + + nodeElement.simulate('mouseenter') + + const tooltip = wrapper.find(CustomTooltip) + expect(tooltip.exists()).toBeTruthy() + expect(tooltip.text()).toEqual(`Custom: ${node.id}`) + + nodeElement.simulate('mouseleave') + expect(wrapper.find(CustomTooltip).exists()).toBeFalsy() + }) + }) +}) + +describe('interactivity', () => { + it('onClick', () => { + const onClick = jest.fn() + const wrapper = mount() + + sampleData.nodes.forEach(node => { + wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('click') + + expect(onClick).toHaveBeenCalledTimes(1) + const [datum] = onClick.mock.calls[0] + expect(datum.id).toEqual(node.id) + + onClick.mockClear() + }) + }) + + it('onMouseEnter', () => { + const onMouseEnter = jest.fn() + const wrapper = mount() + + sampleData.nodes.forEach(node => { + wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('mouseenter') + + expect(onMouseEnter).toHaveBeenCalledTimes(1) + const [datum] = onMouseEnter.mock.calls[0] + expect(datum.id).toEqual(node.id) + + onMouseEnter.mockClear() + }) + }) + + it('onMouseMove handler', () => { + const onMouseMove = jest.fn() + const wrapper = mount() + + sampleData.nodes.forEach(node => { + wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('mousemove') + + expect(onMouseMove).toHaveBeenCalledTimes(1) + const [datum] = onMouseMove.mock.calls[0] + expect(datum.id).toEqual(node.id) + + onMouseMove.mockClear() + }) + }) + + it('onMouseLeave handler', () => { + const onMouseLeave = jest.fn() + const wrapper = mount() + + sampleData.nodes.forEach(node => { + wrapper.find(`circle[data-testid='node.${node.id}']`).simulate('mouseleave') + + expect(onMouseLeave).toHaveBeenCalledTimes(1) + const [datum] = onMouseLeave.mock.calls[0] + expect(datum.id).toEqual(node.id) + + onMouseLeave.mockClear() + }) + }) + + it('disabled if non interactive', () => { + const onClick = jest.fn() + const onMouseEnter = jest.fn() + const onMouseMove = jest.fn() + const onMouseLeave = jest.fn() + + const wrapper = mount( + + ) + + sampleData.nodes.forEach(node => { + const nodeElement = wrapper.find(`circle[data-testid='node.${node.id}']`) + + nodeElement.simulate('mouseenter') + expect(onMouseEnter).not.toHaveBeenCalled() + + nodeElement.simulate('mousemove') + expect(onMouseMove).not.toHaveBeenCalled() + + nodeElement.simulate('mouseleave') + expect(onMouseLeave).not.toHaveBeenCalled() + + nodeElement.simulate('click') + expect(onClick).not.toHaveBeenCalled() + }) + }) +}) + +describe('layers', () => { + it('custom order', () => { + const wrapper = mount() + + const layers = wrapper.find('svg > g').children() + expect(layers.at(0).is('NetworkNodes')).toBeTruthy() + expect(layers.at(1).is('NetworkLinks')).toBeTruthy() + }) + + it('custom layer', () => { + const CustomLayer = () => null + const wrapper = mount() + + const customLayer = wrapper.find(CustomLayer) + expect(customLayer.exists()).toBeTruthy() + + const customLayerProps = customLayer.props() + expect(customLayerProps).toHaveProperty('nodes') + expect(customLayerProps).toHaveProperty('links') + expect(customLayerProps).toHaveProperty('activeNodeIds') + expect(customLayerProps).toHaveProperty('setActiveNodeIds') + }) +}) + +describe('annotations', () => { + it('circle annotation using id', () => { + const annotatedNodeId = 'C' + const wrapper = mount( + + ) + + const annotation = wrapper.find(Annotation) + expect(annotation.exists()).toBeTruthy() + + const annotatedNode = wrapper.find(`circle[data-testid='node.${annotatedNodeId}']`) + const [nodeX, nodeY] = Array.from( + annotatedNode.prop('transform').match(/translate\(([0-9.]+),([0-9.]+)\)/) + ) + .slice(1) + .map(Number) + + expect(annotation.find('circle').first().prop('cx')).toEqual(nodeX) + expect(annotation.find('circle').first().prop('cy')).toEqual(nodeY) + }) +}) + +describe('accessibility', () => { + it('aria properties', () => { + const wrapper = mount( + + ) + + const svg = wrapper.find('svg') + + expect(svg.prop('aria-label')).toBe('AriaLabel') + expect(svg.prop('aria-labelledby')).toBe('AriaLabelledBy') + expect(svg.prop('aria-describedby')).toBe('AriaDescribedBy') + }) }) diff --git a/website/src/components/controls/AnnotationsControl.tsx b/website/src/components/controls/AnnotationsControl.tsx index e2b00aa8d1..35b48e5ee4 100644 --- a/website/src/components/controls/AnnotationsControl.tsx +++ b/website/src/components/controls/AnnotationsControl.tsx @@ -46,20 +46,14 @@ export const AnnotationsControl = memo( control: ArrayControlConfig> } = useMemo( () => ({ - key: property.key, - group: property.group, - help: property.help, - type: property.type, - required: property.required, - flavors: property.flavors, - defaultValue: property.defaultValue, + ...property, control: { type: 'array', shouldCreate: true, addLabel: 'add annotation', shouldRemove: true, getItemTitle: (index, annotation) => - `annotation[${index}] '${annotation.note}' (${annotation.type})`, + `annotations[${index}] '${annotation.note}' (${annotation.type})`, defaults: createDefaults, props: [ { diff --git a/website/src/data/components/network/props.ts b/website/src/data/components/network/props.ts index 94a9f635d2..150ec1abb2 100644 --- a/website/src/data/components/network/props.ts +++ b/website/src/data/components/network/props.ts @@ -290,21 +290,21 @@ const props: ChartProperty[] = [ group: 'Interactivity', help: 'onMouseEnter handler.', type: '(node: ComputedNode, event: MouseEvent) => void', - flavors: allFlavors, + flavors: ['svg'], }, { key: 'onMouseMove', group: 'Interactivity', help: 'onMouseMove handler.', type: '(node: ComputedNode, event: MouseEvent) => void', - flavors: allFlavors, + flavors: ['svg'], }, { key: 'onMouseLeave', group: 'Interactivity', help: 'onMouseLeave handler.', type: '(node: ComputedNode, event: MouseEvent) => void', - flavors: allFlavors, + flavors: ['svg'], }, annotations({ target: 'nodes',