Exported from VizHub: React & D3 Starter.
This viz is a starter that demonstrates patterns used in real world projects in which complexity is anticipated to scale.
Any questions? Ask on Twitter!
import { App } from './App';
import ReactDOM from 'react-dom';
ReactDOM.render(
<App outputPath="" />,
document.getElementById('root')
);
<!DOCTYPE html><html><head>
<title>React & D3 Starter</title>
<link rel="stylesheet" href="styles.css">
<script src="https://unpkg.com/d3@7.4.4/dist/d3.min.js"></script><script src="https://unpkg.com/react@18.1.0/umd/react.production.min.js"></script><script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.production.min.js"></script></head>
<body>
<div id="root"></div>
<script src="bundle.js"></script></body></html>
import { useRef, useEffect } from 'react';
import { select } from 'd3';
import { viz } from './viz';
export const VizWrapper = ({ data }) => {
const ref = useRef();
const width = window.innerWidth;
const height = window.innerHeight;
useEffect(() => {
viz(select(ref.current), {
data,
xValue: (d) => d.sepal_length,
yValue: (d) => d.petal_length,
width,
height,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 40,
},
circleRadius: 10,
});
}, [data]);
return (
<svg
width={width}
height={height}
ref={ref}
/>
);
};
import { axisLeft, axisBottom } from 'd3';
export const axes = (
selection,
{ height, margin, xScale, yScale }
) => {
const { left, bottom } = margin;
selection
.selectAll('g.axis-y')
.data([null])
.join('g')
.attr('class', 'axis axis-y')
.attr('transform', `translate(${left},0)`)
.call(axisLeft(yScale));
selection
.selectAll('g.axis-x')
.data([null])
.join('g')
.attr('class', 'axis axis-x')
.attr(
'transform',
`translate(0,${height - bottom})`
)
.call(axisBottom(xScale));
};
(function (react, d3, ReactDOM) {
'use strict';
ReactDOM = ReactDOM && Object.prototype.hasOwnProperty.call(ReactDOM, 'default') ? ReactDOM['default'] : ReactDOM;
const parseRow = (d) => {
d.sepal_length = +d.sepal_length;
d.sepal_width = +d.sepal_width;
d.petal_length = +d.petal_length;
d.petal_width = +d.petal_width;
return d;
};
const useData = (outputPath) => {
const [data, setData] = react.useState(null);
react.useEffect(async () => {
setData(await d3.csv('data.csv', parseRow));
}, []);
return data;
};
const axes = (
selection,
{ height, margin, xScale, yScale }
) => {
const { left, bottom } = margin;
selection
.selectAll('g.axis-y')
.data([null])
.join('g')
.attr('class', 'axis axis-y')
.attr('transform', `translate(${left},0)`)
.call(d3.axisLeft(yScale));
selection
.selectAll('g.axis-x')
.data([null])
.join('g')
.attr('class', 'axis axis-x')
.attr(
'transform',
`translate(0,${height - bottom})`
)
.call(d3.axisBottom(xScale));
};
const viz = (
selection,
{
data,
xValue,
yValue,
width,
height,
margin,
circleRadius,
}
) => {
const { top, right, bottom, left } = margin;
const xScale = d3.scaleLinear()
.domain(d3.extent(data, xValue))
.range([left, width - right]);
const yScale = d3.scaleLinear()
.domain(d3.extent(data, yValue))
.range([height - bottom, top]);
selection
.selectAll('circle')
.data(data)
.join('circle')
.attr('r', circleRadius)
.attr('fill-opacity', 0.5)
.attr('cx', (d) => xScale(xValue(d)))
.attr('cy', (d) => yScale(yValue(d)));
axes(selection, {
height,
margin,
xScale,
yScale,
});
};
const VizWrapper = ({ data }) => {
const ref = react.useRef();
const width = window.innerWidth;
const height = window.innerHeight;
react.useEffect(() => {
viz(d3.select(ref.current), {
data,
xValue: (d) => d.sepal_length,
yValue: (d) => d.petal_length,
width,
height,
margin: {
top: 20,
right: 20,
bottom: 40,
left: 40,
},
circleRadius: 10,
});
}, [data]);
return (
React.createElement( 'svg', {
width: width, height: height, ref: ref })
);
};
const App = () => {
const data = useData();
return data ? (
React.createElement( VizWrapper, { data: data })
) : (
// Could be expanded with a real loading indicator.
'Loading...'
);
};
ReactDOM.render(
React.createElement( App, { outputPath: "" }),
document.getElementById('root')
);
}(React, d3, ReactDOM));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VzIjpbInVzZURhdGEuanMiLCJheGVzLmpzIiwidml6LmpzIiwiVml6V3JhcHBlci5qcyIsIkFwcC5qcyIsImluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHVzZVN0YXRlLCB1c2VFZmZlY3QgfSBmcm9tICdyZWFjdCc7XG5pbXBvcnQgeyBjc3YgfSBmcm9tICdkMyc7XG5cbmNvbnN0IHBhcnNlUm93ID0gKGQpID0+IHtcbiAgZC5zZXBhbF9sZW5ndGggPSArZC5zZXBhbF9sZW5ndGg7XG4gIGQuc2VwYWxfd2lkdGggPSArZC5zZXBhbF93aWR0aDtcbiAgZC5wZXRhbF9sZW5ndGggPSArZC5wZXRhbF9sZW5ndGg7XG4gIGQucGV0YWxfd2lkdGggPSArZC5wZXRhbF93aWR0aDtcbiAgcmV0dXJuIGQ7XG59O1xuXG5leHBvcnQgY29uc3QgdXNlRGF0YSA9IChvdXRwdXRQYXRoKSA9PiB7XG4gIGNvbnN0IFtkYXRhLCBzZXREYXRhXSA9IHVzZVN0YXRlKG51bGwpO1xuXG4gIHVzZUVmZmVjdChhc3luYyAoKSA9PiB7XG4gICAgc2V0RGF0YShhd2FpdCBjc3YoJ2RhdGEuY3N2JywgcGFyc2VSb3cpKTtcbiAgfSwgW10pO1xuXG4gIHJldHVybiBkYXRhO1xufTtcbiIsImltcG9ydCB7IGF4aXNMZWZ0LCBheGlzQm90dG9tIH0gZnJvbSAnZDMnO1xuXG5leHBvcnQgY29uc3QgYXhlcyA9IChcbiAgc2VsZWN0aW9uLFxuICB7IGhlaWdodCwgbWFyZ2luLCB4U2NhbGUsIHlTY2FsZSB9XG4pID0+IHtcbiAgY29uc3QgeyBsZWZ0LCBib3R0b20gfSA9IG1hcmdpbjtcblxuICBzZWxlY3Rpb25cbiAgICAuc2VsZWN0QWxsKCdnLmF4aXMteScpXG4gICAgLmRhdGEoW251bGxdKVxuICAgIC5qb2luKCdnJylcbiAgICAuYXR0cignY2xhc3MnLCAnYXhpcyBheGlzLXknKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBgdHJhbnNsYXRlKCR7bGVmdH0sMClgKVxuICAgIC5jYWxsKGF4aXNMZWZ0KHlTY2FsZSkpO1xuXG4gIHNlbGVjdGlvblxuICAgIC5zZWxlY3RBbGwoJ2cuYXhpcy14JylcbiAgICAuZGF0YShbbnVsbF0pXG4gICAgLmpvaW4oJ2cnKVxuICAgIC5hdHRyKCdjbGFzcycsICdheGlzIGF4aXMteCcpXG4gICAgLmF0dHIoXG4gICAgICAndHJhbnNmb3JtJyxcbiAgICAgIGB0cmFuc2xhdGUoMCwke2hlaWdodCAtIGJvdHRvbX0pYFxuICAgIClcbiAgICAuY2FsbChheGlzQm90dG9tKHhTY2FsZSkpO1xufTtcbiIsImltcG9ydCB7IHNjYWxlTGluZWFyLCBleHRlbnQgfSBmcm9tICdkMyc7XG5pbXBvcnQgeyBheGVzIH0gZnJvbSAnLi9heGVzJztcblxuZXhwb3J0IGNvbnN0IHZpeiA9IChcbiAgc2VsZWN0aW9uLFxuICB7XG4gICAgZGF0YSxcbiAgICB4VmFsdWUsXG4gICAgeVZhbHVlLFxuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBtYXJnaW4sXG4gICAgY2lyY2xlUmFkaXVzLFxuICB9XG4pID0+IHtcbiAgY29uc3QgeyB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQgfSA9IG1hcmdpbjtcblxuICBjb25zdCB4U2NhbGUgPSBzY2FsZUxpbmVhcigpXG4gICAgLmRvbWFpbihleHRlbnQoZGF0YSwgeFZhbHVlKSlcbiAgICAucmFuZ2UoW2xlZnQsIHdpZHRoIC0gcmlnaHRdKTtcblxuICBjb25zdCB5U2NhbGUgPSBzY2FsZUxpbmVhcigpXG4gICAgLmRvbWFpbihleHRlbnQoZGF0YSwgeVZhbHVlKSlcbiAgICAucmFuZ2UoW2hlaWdodCAtIGJvdHRvbSwgdG9wXSk7XG5cbiAgc2VsZWN0aW9uXG4gICAgLnNlbGVjdEFsbCgnY2lyY2xlJylcbiAgICAuZGF0YShkYXRhKVxuICAgIC5qb2luKCdjaXJjbGUnKVxuICAgIC5hdHRyKCdyJywgY2lyY2xlUmFkaXVzKVxuICAgIC5hdHRyKCdmaWxsLW9wYWNpdHknLCAwLjUpXG4gICAgLmF0dHIoJ2N4JywgKGQpID0+IHhTY2FsZSh4VmFsdWUoZCkpKVxuICAgIC5hdHRyKCdjeScsIChkKSA9PiB5U2NhbGUoeVZhbHVlKGQpKSk7XG5cbiAgYXhlcyhzZWxlY3Rpb24sIHtcbiAgICBoZWlnaHQsXG4gICAgbWFyZ2luLFxuICAgIHhTY2FsZSxcbiAgICB5U2NhbGUsXG4gIH0pO1xufTtcbiIsImltcG9ydCB7IHVzZVJlZiwgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnO1xuaW1wb3J0IHsgc2VsZWN0IH0gZnJvbSAnZDMnO1xuaW1wb3J0IHsgdml6IH0gZnJvbSAnLi92aXonO1xuXG5leHBvcnQgY29uc3QgVml6V3JhcHBlciA9ICh7IGRhdGEgfSkgPT4ge1xuICBjb25zdCByZWYgPSB1c2VSZWYoKTtcblxuICBjb25zdCB3aWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoO1xuICBjb25zdCBoZWlnaHQgPSB3aW5kb3cuaW5uZXJIZWlnaHQ7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICB2aXooc2VsZWN0KHJlZi5jdXJyZW50KSwge1xuICAgICAgZGF0YSxcbiAgICAgIHhWYWx1ZTogKGQpID0+IGQuc2VwYWxfbGVuZ3RoLFxuICAgICAgeVZhbHVlOiAoZCkgPT4gZC5wZXRhbF9sZW5ndGgsXG4gICAgICB3aWR0aCxcbiAgICAgIGhlaWdodCxcbiAgICAgIG1hcmdpbjoge1xuICAgICAgICB0b3A6IDIwLFxuICAgICAgICByaWdodDogMjAsXG4gICAgICAgIGJvdHRvbTogNDAsXG4gICAgICAgIGxlZnQ6IDQwLFxuICAgICAgfSxcbiAgICAgIGNpcmNsZVJhZGl1czogMTAsXG4gICAgfSk7XG4gIH0sIFtkYXRhXSk7XG5cbiAgcmV0dXJuIChcbiAgICA8c3ZnXG4gICAgICB3aWR0aD17d2lkdGh9XG4gICAgICBoZWlnaHQ9e2hlaWdodH1cbiAgICAgIHJlZj17cmVmfVxuICAgIC8+XG4gICk7XG59O1xuIiwiaW1wb3J0IHt1c2VEYXRhfSBmcm9tICcuL3VzZURhdGEnO1xuaW1wb3J0IHtWaXpXcmFwcGVyfSBmcm9tICcuL1ZpeldyYXBwZXInO1xuXG5leHBvcnQgY29uc3QgQXBwID0gKCkgPT4ge1xuICBjb25zdCBkYXRhID0gdXNlRGF0YSgpO1xuICByZXR1cm4gZGF0YSA/IChcbiAgICA8Vml6V3JhcHBlciBkYXRhPXtkYXRhfSAvPlxuICApIDogKFxuICAgIC8vIENvdWxkIGJlIGV4cGFuZGVkIHdpdGggYSByZWFsIGxvYWRpbmcgaW5kaWNhdG9yLlxuICAgICdMb2FkaW5nLi4uJ1xuICApO1xufTtcbiIsImltcG9ydCB7IEFwcCB9IGZyb20gJy4vQXBwJztcbmltcG9ydCBSZWFjdERPTSBmcm9tICdyZWFjdC1kb20nO1xuXG5SZWFjdERPTS5yZW5kZXIoXG4gIDxBcHAgb3V0cHV0UGF0aD1cIlwiIC8+LFxuICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgncm9vdCcpXG4pO1xuIl0sIm5hbWVzIjpbInVzZVN0YXRlIiwidXNlRWZmZWN0IiwiY3N2IiwiYXhpc0xlZnQiLCJheGlzQm90dG9tIiwic2NhbGVMaW5lYXIiLCJleHRlbnQiLCJ1c2VSZWYiLCJzZWxlY3QiXSwibWFwcGluZ3MiOiI7Ozs7O0VBR0EsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDLEtBQUs7RUFDeEIsRUFBRSxDQUFDLENBQUMsWUFBWSxHQUFHLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQztFQUNuQyxFQUFFLENBQUMsQ0FBQyxXQUFXLEdBQUcsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDO0VBQ2pDLEVBQUUsQ0FBQyxDQUFDLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUM7RUFDbkMsRUFBRSxDQUFDLENBQUMsV0FBVyxHQUFHLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQztFQUNqQyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0VBQ1gsQ0FBQyxDQUFDO0FBQ0Y7RUFDTyxNQUFNLE9BQU8sR0FBRyxDQUFDLFVBQVUsS0FBSztFQUN2QyxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsT0FBTyxD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
sepal_length,sepal_width,petal_length,petal_width,species
5.1,3.5,1.4,0.2,setosa
4.9,3.0,1.4,0.2,setosa
4.7,3.2,1.3,0.2,setosa
4.6,3.1,1.5,0.2,setosa
5.0,3.6,1.4,0.2,setosa
5.4,3.9,1.7,0.4,setosa
4.6,3.4,1.4,0.3,setosa
5.0,3.4,1.5,0.2,setosa
4.4,2.9,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.4,3.7,1.5,0.2,setosa
4.8,3.4,1.6,0.2,setosa
4.8,3.0,1.4,0.1,setosa
4.3,3.0,1.1,0.1,setosa
5.8,4.0,1.2,0.2,setosa
5.7,4.4,1.5,0.4,setosa
5.4,3.9,1.3,0.4,setosa
5.1,3.5,1.4,0.3,setosa
5.7,3.8,1.7,0.3,setosa
5.1,3.8,1.5,0.3,setosa
5.4,3.4,1.7,0.2,setosa
5.1,3.7,1.5,0.4,setosa
4.6,3.6,1.0,0.2,setosa
5.1,3.3,1.7,0.5,setosa
4.8,3.4,1.9,0.2,setosa
5.0,3.0,1.6,0.2,setosa
5.0,3.4,1.6,0.4,setosa
5.2,3.5,1.5,0.2,setosa
5.2,3.4,1.4,0.2,setosa
4.7,3.2,1.6,0.2,setosa
4.8,3.1,1.6,0.2,setosa
5.4,3.4,1.5,0.4,setosa
5.2,4.1,1.5,0.1,setosa
5.5,4.2,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.0,3.2,1.2,0.2,setosa
5.5,3.5,1.3,0.2,setosa
4.9,3.1,1.5,0.1,setosa
4.4,3.0,1.3,0.2,setosa
5.1,3.4,1.5,0.2,setosa
5.0,3.5,1.3,0.3,setosa
4.5,2.3,1.3,0.3,setosa
4.4,3.2,1.3,0.2,setosa
5.0,3.5,1.6,0.6,setosa
5.1,3.8,1.9,0.4,setosa
4.8,3.0,1.4,0.3,setosa
5.1,3.8,1.6,0.2,setosa
4.6,3.2,1.4,0.2,setosa
5.3,3.7,1.5,0.2,setosa
5.0,3.3,1.4,0.2,setosa
7.0,3.2,4.7,1.4,versicolor
6.4,3.2,4.5,1.5,versicolor
6.9,3.1,4.9,1.5,versicolor
5.5,2.3,4.0,1.3,versicolor
6.5,2.8,4.6,1.5,versicolor
5.7,2.8,4.5,1.3,versicolor
6.3,3.3,4.7,1.6,versicolor
4.9,2.4,3.3,1.0,versicolor
6.6,2.9,4.6,1.3,versicolor
5.2,2.7,3.9,1.4,versicolor
5.0,2.0,3.5,1.0,versicolor
5.9,3.0,4.2,1.5,versicolor
6.0,2.2,4.0,1.0,versicolor
6.1,2.9,4.7,1.4,versicolor
5.6,2.9,3.6,1.3,versicolor
6.7,3.1,4.4,1.4,versicolor
5.6,3.0,4.5,1.5,versicolor
5.8,2.7,4.1,1.0,versicolor
6.2,2.2,4.5,1.5,versicolor
5.6,2.5,3.9,1.1,versicolor
5.9,3.2,4.8,1.8,versicolor
6.1,2.8,4.0,1.3,versicolor
6.3,2.5,4.9,1.5,versicolor
6.1,2.8,4.7,1.2,versicolor
6.4,2.9,4.3,1.3,versicolor
6.6,3.0,4.4,1.4,versicolor
6.8,2.8,4.8,1.4,versicolor
6.7,3.0,5.0,1.7,versicolor
6.0,2.9,4.5,1.5,versicolor
5.7,2.6,3.5,1.0,versicolor
5.5,2.4,3.8,1.1,versicolor
5.5,2.4,3.7,1.0,versicolor
5.8,2.7,3.9,1.2,versicolor
6.0,2.7,5.1,1.6,versicolor
5.4,3.0,4.5,1.5,versicolor
6.0,3.4,4.5,1.6,versicolor
6.7,3.1,4.7,1.5,versicolor
6.3,2.3,4.4,1.3,versicolor
5.6,3.0,4.1,1.3,versicolor
5.5,2.5,4.0,1.3,versicolor
5.5,2.6,4.4,1.2,versicolor
6.1,3.0,4.6,1.4,versicolor
5.8,2.6,4.0,1.2,versicolor
5.0,2.3,3.3,1.0,versicolor
5.6,2.7,4.2,1.3,versicolor
5.7,3.0,4.2,1.2,versicolor
5.7,2.9,4.2,1.3,versicolor
6.2,2.9,4.3,1.3,versicolor
5.1,2.5,3.0,1.1,versicolor
5.7,2.8,4.1,1.3,versicolor
6.3,3.3,6.0,2.5,virginica
5.8,2.7,5.1,1.9,virginica
7.1,3.0,5.9,2.1,virginica
6.3,2.9,5.6,1.8,virginica
6.5,3.0,5.8,2.2,virginica
7.6,3.0,6.6,2.1,virginica
4.9,2.5,4.5,1.7,virginica
7.3,2.9,6.3,1.8,virginica
6.7,2.5,5.8,1.8,virginica
7.2,3.6,6.1,2.5,virginica
6.5,3.2,5.1,2.0,virginica
6.4,2.7,5.3,1.9,virginica
6.8,3.0,5.5,2.1,virginica
5.7,2.5,5.0,2.0,virginica
5.8,2.8,5.1,2.4,virginica
6.4,3.2,5.3,2.3,virginica
6.5,3.0,5.5,1.8,virginica
7.7,3.8,6.7,2.2,virginica
7.7,2.6,6.9,2.3,virginica
6.0,2.2,5.0,1.5,virginica
6.9,3.2,5.7,2.3,virginica
5.6,2.8,4.9,2.0,virginica
7.7,2.8,6.7,2.0,virginica
6.3,2.7,4.9,1.8,virginica
6.7,3.3,5.7,2.1,virginica
7.2,3.2,6.0,1.8,virginica
6.2,2.8,4.8,1.8,virginica
6.1,3.0,4.9,1.8,virginica
6.4,2.8,5.6,2.1,virginica
7.2,3.0,5.8,1.6,virginica
7.4,2.8,6.1,1.9,virginica
7.9,3.8,6.4,2.0,virginica
6.4,2.8,5.6,2.2,virginica
6.3,2.8,5.1,1.5,virginica
6.1,2.6,5.6,1.4,virginica
7.7,3.0,6.1,2.3,virginica
6.3,3.4,5.6,2.4,virginica
6.4,3.1,5.5,1.8,virginica
6.0,3.0,4.8,1.8,virginica
6.9,3.1,5.4,2.1,virginica
6.7,3.1,5.6,2.4,virginica
6.9,3.1,5.1,2.3,virginica
5.8,2.7,5.1,1.9,virginica
6.8,3.2,5.9,2.3,virginica
6.7,3.3,5.7,2.5,virginica
6.7,3.0,5.2,2.3,virginica
6.3,2.5,5.0,1.9,virginica
6.5,3.0,5.2,2.0,virginica
6.2,3.4,5.4,2.3,virginica
5.9,3.0,5.1,1.8,virginica
{
"scripts": {
"build": "rollup -c"
},
"devDependencies": {
"rollup": "latest",
"@rollup/plugin-buble": "latest"
}
}
const buble = require('@rollup/plugin-buble');
export default {
input: 'index.js',
external: ["d3","react","react-dom"],
output: {
file: 'bundle.js',
format: 'iife',
sourcemap: true,
globals: {"d3":"d3","react":"React","react-dom":"ReactDOM"}
},
plugins: [buble()]
};
body {
margin: 0;
overflow: hidden;
}
.message {
font-size: 13em;
text-align: center;
}
import { useState, useEffect } from 'react';
import { csv } from 'd3';
const parseRow = (d) => {
d.sepal_length = +d.sepal_length;
d.sepal_width = +d.sepal_width;
d.petal_length = +d.petal_length;
d.petal_width = +d.petal_width;
return d;
};
export const useData = (outputPath) => {
const [data, setData] = useState(null);
useEffect(async () => {
setData(await csv('data.csv', parseRow));
}, []);
return data;
};
import { scaleLinear, extent } from 'd3';
import { axes } from './axes';
export const viz = (
selection,
{
data,
xValue,
yValue,
width,
height,
margin,
circleRadius,
}
) => {
const { top, right, bottom, left } = margin;
const xScale = scaleLinear()
.domain(extent(data, xValue))
.range([left, width - right]);
const yScale = scaleLinear()
.domain(extent(data, yValue))
.range([height - bottom, top]);
selection
.selectAll('circle')
.data(data)
.join('circle')
.attr('r', circleRadius)
.attr('fill-opacity', 0.5)
.attr('cx', (d) => xScale(xValue(d)))
.attr('cy', (d) => yScale(yValue(d)));
axes(selection, {
height,
margin,
xScale,
yScale,
});
};