Note: I tried testing ChatGPT to produce more complex code! But this one failed miserably. Read on.
Rendering a Sankey diagram in React using SVG can be a complex task as it involves creating paths and managing node positions. There are libraries available that simplify the process, such as react-sankey
or d3-sankey
. However, if you prefer to implement it manually, here’s an example code snippet to get you started:
import React from 'react';
const SankeyDiagram = ({ data, width, height }) => {
const { nodes, links } = data;
const nodeWidth = 20;
const nodePadding = 8;
const maxLinkValue = Math.max(...links.map(link => link.value));
const linkScale = width / maxLinkValue;
const getNodeX = node => {
return node.index * (nodeWidth + nodePadding);
};
const getNodeY = node => {
return height / 2 - node.value / 2;
};
const renderNode = node => {
const x = getNodeX(node);
const y = getNodeY(node);
return (
<rect
key={node.id}
x={x}
y={y}
width={nodeWidth}
height={node.value}
fill={node.color}
/>
);
};
const renderLink = link => {
const x1 = getNodeX(link.source) + nodeWidth;
const y1 = getNodeY(link.source) + link.sy + link.dy / 2;
const x2 = getNodeX(link.target);
const y2 = getNodeY(link.target) + link.ty + link.dy / 2;
return (
<path
key={`${link.source.id}-${link.target.id}`}
d={`M ${x1},${y1} L ${x2},${y2}`}
stroke={link.color}
strokeWidth={link.value * linkScale}
fill="none"
/>
);
};
return (
<svg width={width} height={height}>
{nodes.map(renderNode)}
{links.map(renderLink)}
</svg>
);
};
export default SankeyDiagram;
In this code, the SankeyDiagram
component receives the following props:
data
: An object containing an array of nodes and an array of links. Each node has anid
,value
, andcolor
. Each link hassource
,target
,value
, andcolor
.width
: The width of the chart in pixels.height
: The height of the chart in pixels.
The component calculates the necessary dimensions for each node and link based on the provided props. It then maps over the nodes
array to generate SVG rect
elements for each node. The renderNode
function calculates the position and dimensions of each node based on the index and value.
The component also maps over the links
array to generate SVG path
elements for each link. The renderLink
function calculates the positions of the source and target nodes and sets the stroke width of the path based on the link value.
Finally, the component renders an SVG element with the specified width and height. It renders the nodes and links using the nodes.map
and links.map
functions respectively.
To use the SankeyDiagram
component, provide the appropriate data, width, and height as props. For example:
import React from 'react';
import SankeyDiagram from './SankeyDiagram';
const App = () => {
const data = {
nodes: [
{ id: 'A', value: 100, color: 'steelblue' },
{ id: 'B', value: 200, color: 'orange' },
{ id: 'C', value: 150, color: 'green' },
],
links: [
{ source: 'A', target: 'B', value: 50, color: 'gray' },
{ source: 'B', target: 'C', value: 100, color: 'gray' },
{ source: 'A', target: 'C', value: 75, color: 'gray' },
],
};
return (
<div>
<h1>Sankey Diagram Example</h1>
<SankeyDiagram data={data} width={500} height={300} />
</div>
);
};
export default App;
In this example, we provide an object with an array of nodes and an array of links to the data
prop of the SankeyDiagram
component. Adjust the values and colors to match your specific data.
On running this code in CodeSandbox, I get this image!
Clearly, this image is wrong. So, where is the code wrong?
In the below code, node does not have an index
const getNodeX = node => {
return node.index * (nodeWidth + nodePadding);
};
In the renderLink fn, there is no “sy”, “dy”, “ty”. link.source, link.target does not refer to a node.
const renderLink = link => {
const x1 = getNodeX(link.source) + nodeWidth;
const y1 = getNodeY(link.source) + link.sy + link.dy / 2;
const x2 = getNodeX(link.target);
const y2 = getNodeY(link.target) + link.ty + link.dy / 2;
return (
<path
key={`${link.source.id}-${link.target.id}`}
d={`M ${x1},${y1} L ${x2},${y2}`}
stroke={link.color}
strokeWidth={link.value * linkScale}
fill="none"
/>
);
};
Even after correcting those errors in CodeSandbox, I still get a bad sankey diagram
Sankey diagrams are difficult to do in React because of lack of library options. Here is an example of a good sankey diagram, if you have not seen one.
After finding the mistakes in ChatGPT generated code, I am happy that my job is safe from GPT code for the next 10 years.