Skip to content

Commit

Permalink
feat(network): add support for tooltips (#1080)
Browse files Browse the repository at this point in the history
  • Loading branch information
tg970 authored Aug 4, 2020
1 parent daebd61 commit 4d8e822
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 6 deletions.
1 change: 1 addition & 0 deletions .storybook/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ function loadStories() {
require('../packages/heatmap/stories/heatmap.stories')
require('../packages/line/stories/line.stories')
require('../packages/line/stories/LineCanvas.stories')
require('../packages/network/stories/network.stories')
require('../packages/pie/stories/pie.stories')
require('../packages/radar/stories/radar.stories')
require('../packages/sankey/stories/sankey.stories')
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

## Setup

Nivo is structured into multiple packages handled by [lerna](https://lernajs.io/).
Nivo is structured into multiple packages handled by [lerna](https://lerna.js.org/).
In order to install all the required dependencies and to establish links between
the various packages, please execute the following:

Expand Down
13 changes: 12 additions & 1 deletion packages/network/src/AnimatedNodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ const willLeave = springConfig => ({ style }) => ({
scale: spring(0, springConfig),
})

const AnimatedNodes = ({ nodes, color, borderWidth, borderColor }) => {
const AnimatedNodes = ({
nodes,
color,
borderWidth,
borderColor,
handleNodeHover,
handleNodeLeave,
}) => {
const { springConfig } = useMotionConfig()

return (
Expand Down Expand Up @@ -58,6 +65,8 @@ const AnimatedNodes = ({ nodes, color, borderWidth, borderColor }) => {
borderWidth={borderWidth}
borderColor={borderColor(node)}
scale={Math.max(style.scale, 0)}
handleNodeHover={handleNodeHover}
handleNodeLeave={handleNodeLeave}
/>
)
})}
Expand All @@ -72,6 +81,8 @@ AnimatedNodes.propTypes = {
color: PropTypes.func.isRequired,
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.func.isRequired,
handleNodeHover: PropTypes.func.isRequired,
handleNodeLeave: PropTypes.func.isRequired,
}

export default memo(AnimatedNodes)
21 changes: 20 additions & 1 deletion packages/network/src/Network.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import React, { Fragment } from 'react'
import React, { Fragment, useCallback } from 'react'
import { withContainer, useDimensions, SvgWrapper, useTheme, useMotionConfig } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { useTooltip } from '@nivo/tooltip'
import { NetworkPropTypes, NetworkDefaultProps } from './props'
import { useNetwork, useNodeColor, useLinkThickness } from './hooks'
import AnimatedNodes from './AnimatedNodes'
import StaticNodes from './StaticNodes'
import AnimatedLinks from './AnimatedLinks'
import StaticLinks from './StaticLinks'
import NetworkNodeTooltip from './NetworkNodeTooltip'

const Network = props => {
const {
Expand All @@ -39,6 +41,8 @@ const Network = props => {

linkThickness,
linkColor,
tooltip,
isInteractive,
} = props

const { margin, innerWidth, innerHeight, outerWidth, outerHeight } = useDimensions(
Expand All @@ -65,6 +69,19 @@ const Network = props => {
center: [innerWidth / 2, innerHeight / 2],
})

const { showTooltipFromEvent, hideTooltip } = useTooltip()

const handleNodeHover = useCallback(
(node, event) => {
showTooltipFromEvent(<NetworkNodeTooltip node={node} tooltip={tooltip} />, event)
},
[showTooltipFromEvent, tooltip]
)

const handleNodeLeave = useCallback(() => {
hideTooltip()
}, [hideTooltip])

const layerById = {
links: React.createElement(animate === true ? AnimatedLinks : StaticLinks, {
key: 'links',
Expand All @@ -78,6 +95,8 @@ const Network = props => {
color: getColor,
borderWidth: nodeBorderWidth,
borderColor: getBorderColor,
handleNodeHover: isInteractive ? handleNodeHover : undefined,
handleNodeLeave: isInteractive ? handleNodeLeave : undefined,
}),
}

Expand Down
24 changes: 24 additions & 0 deletions packages/network/src/NetworkNodeTooltip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { BasicTooltip } from '@nivo/tooltip'

const NetworkNodeTooltip = ({ node, format, tooltip }) => (
<BasicTooltip
id={node.id}
enableChip={true}
color={node.color}
format={format}
renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...node }) : null}
/>
)

NetworkNodeTooltip.propTypes = {
node: PropTypes.shape({
id: PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
}).isRequired,
format: PropTypes.func,
tooltip: PropTypes.func,
}

export default memo(NetworkNodeTooltip)
18 changes: 17 additions & 1 deletion packages/network/src/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,28 @@
import React, { memo } from 'react'
import PropTypes from 'prop-types'

const Node = ({ x, y, radius, color, borderWidth, borderColor, scale = 1 }) => {
const Node = ({
node,
x,
y,
radius,
color,
borderWidth,
borderColor,
scale = 1,
handleNodeHover,
handleNodeLeave,
}) => {
return (
<circle
transform={`translate(${x},${y}) scale(${scale})`}
r={radius}
fill={color}
strokeWidth={borderWidth}
stroke={borderColor}
onMouseEnter={event => handleNodeHover(node, event)}
onMouseMove={event => handleNodeHover(node, event)}
onMouseLeave={handleNodeLeave}
/>
)
}
Expand All @@ -30,6 +44,8 @@ Node.propTypes = {
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.string.isRequired,
scale: PropTypes.number,
handleNodeHover: PropTypes.func.isRequired,
handleNodeLeave: PropTypes.func.isRequired,
}

export default memo(Node)
13 changes: 12 additions & 1 deletion packages/network/src/StaticNodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import React, { memo } from 'react'
import PropTypes from 'prop-types'
import Node from './Node'

const StaticNodes = ({ nodes, color, borderWidth, borderColor }) => {
const StaticNodes = ({
nodes,
color,
borderWidth,
borderColor,
handleNodeHover,
handleNodeLeave,
}) => {
return nodes.map(node => {
return (
<Node
Expand All @@ -22,6 +29,8 @@ const StaticNodes = ({ nodes, color, borderWidth, borderColor }) => {
color={color(node)}
borderWidth={borderWidth}
borderColor={borderColor(node)}
handleNodeHover={handleNodeHover}
handleNodeLeave={handleNodeLeave}
/>
)
})
Expand All @@ -32,6 +41,8 @@ StaticNodes.propTypes = {
color: PropTypes.func.isRequired,
borderWidth: PropTypes.number.isRequired,
borderColor: PropTypes.func.isRequired,
handleNodeHover: PropTypes.func.isRequired,
handleNodeLeave: PropTypes.func.isRequired,
}

export default memo(StaticNodes)
43 changes: 43 additions & 0 deletions packages/network/stories/network.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { NetworkDefaultProps } from '../src/props'
import { generateData } from '../../../website/src/data/components/network/generator'
import { Network } from '../src'

const data = generateData()

const commonProperties = {
...NetworkDefaultProps,
nodes: data.nodes,
links: data.links,
width: 900,
height: 340,
nodeColor: function (t) {
return t.color
},
repulsivity: 6,
iterations: 60,
}

const stories = storiesOf('Network', module)

stories.add('default', () => <Network {...commonProperties} />)

stories.add('custom tooltip', () => (
<Network
{...commonProperties}
tooltip={node => {
return (
<div>
<div>
<strong style={{ color: node.color }}>ID: {node.id}</strong>
<br />
Depth: {node.depth}
<br />
Radius: {node.radius}
</div>
</div>
)
}}
/>
))
14 changes: 13 additions & 1 deletion website/src/data/components/network/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const props = [
from the corresponding link property, thus, this property
should exist on each link.
If you use a **function**, it will receive a link and must return
If you use a **function**, it will receive a link and must return
the desired distance.
`,
},
Expand Down Expand Up @@ -170,6 +170,18 @@ const props = [
inheritableProperties: ['source.color', 'target.color'],
},
},
{
key: 'tooltip',
group: 'Interactivity',
type: 'Function',
required: false,
help: 'Custom tooltip component.',
description: `
A function allowing complete tooltip customisation,
it must return a valid HTML
element and will receive the node's data.
`,
},
{
key: 'layers',
group: 'Customization',
Expand Down

0 comments on commit 4d8e822

Please sign in to comment.