aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNaafiyan Ahmed <naafiyan@gmail.com>2022-07-14 16:38:00 -0400
committerNaafiyan Ahmed <naafiyan@gmail.com>2022-07-14 16:38:00 -0400
commit9136b17ed9021bd9435117f68ff66c4dbb1a1889 (patch)
tree1a06ebbf3bcd6a6fe637e580e4e4030599169bed
parentee2b15e0baa9cd3847d785c62d75fd82defaee08 (diff)
refactored code into d3 utils
-rw-r--r--src/client/views/nodes/DataVizBox/ChartBox.tsx8
-rw-r--r--src/client/views/nodes/DataVizBox/LineChart.tsx61
-rw-r--r--src/client/views/nodes/DataVizBox/utils/D3Utils.ts59
3 files changed, 81 insertions, 47 deletions
diff --git a/src/client/views/nodes/DataVizBox/ChartBox.tsx b/src/client/views/nodes/DataVizBox/ChartBox.tsx
index 7424fbe5f..a03aaae31 100644
--- a/src/client/views/nodes/DataVizBox/ChartBox.tsx
+++ b/src/client/views/nodes/DataVizBox/ChartBox.tsx
@@ -23,7 +23,7 @@ export interface RechartData {
y: number;
}
-export interface DataPoints {
+export interface DataPoint {
x: number;
y: number;
}
@@ -31,8 +31,8 @@ export interface DataPoints {
export interface ChartData {
xLabel: string;
yLabel: string;
- data: DataPoints[];
- tooltipContent: (data: DataPoints) => string;
+ data: DataPoint[];
+ tooltipContent: (data: DataPoint) => string;
}
@observer
@@ -65,7 +65,7 @@ export class ChartBox extends React.Component<ChartBoxProps> {
xLabel: '',
yLabel: '',
data: [],
- tooltipContent: (data: DataPoints) => {
+ tooltipContent: (data: DataPoint) => {
return `<b>x: ${data.x} y: ${data.y}</b>`;
},
};
diff --git a/src/client/views/nodes/DataVizBox/LineChart.tsx b/src/client/views/nodes/DataVizBox/LineChart.tsx
index 5ad8f0846..8a757866e 100644
--- a/src/client/views/nodes/DataVizBox/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/LineChart.tsx
@@ -1,9 +1,10 @@
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { ChartData, DataPoints } from './ChartBox';
+import { ChartData, DataPoint } from './ChartBox';
// import d3
import * as d3 from 'd3';
+import { minMaxRange, createLineGenerator, xGrid, yGrid, drawLine, scaleCreator } from './utils/D3Utils';
interface LineChartProps {
data: ChartData;
@@ -23,6 +24,8 @@ export class LineChart extends React.Component<LineChartProps> {
@observable private _x: number = 0;
@observable private _y: number = 0;
@observable private _currSelected: SelectedDataPoint | undefined = undefined;
+ // create ref for the div
+ private _chartRef: React.RefObject<HTMLDivElement> = React.createRef();
componentDidMount() {
console.log('Getting to line chart');
@@ -36,7 +39,7 @@ export class LineChart extends React.Component<LineChartProps> {
}
}
- tooltipContent(data: DataPoints) {
+ tooltipContent(data: DataPoint) {
return `<b>x: ${data.x} y: ${data.y}</b>`;
}
@@ -48,16 +51,10 @@ export class LineChart extends React.Component<LineChartProps> {
const margin = { top: 50, right: 50, bottom: 50, left: 50 };
const { data } = this.props;
- const yMin = d3.min(data.data, d => d.y);
- const yMax = d3.max(data.data, d => d.y);
-
- // TODO: nda - modify data.x to support strings
-
- const xMin = d3.min(data.data, d => d.x);
- const xMax = d3.max(data.data, d => d.x);
+ const { xMin, xMax, yMin, yMax } = minMaxRange(data.data);
// adding svg
- const svg = d3.select('#chart-container').append('svg').attr('width', '100%').attr('height', '100%').append('g').attr('transform', `translate(${margin.left}, ${margin.top})`);
+ const svg = d3.select(this._chartRef.current).append('svg').attr('width', '100%').attr('height', '100%').append('g').attr('transform', `translate(${margin.left}, ${margin.top})`);
// adding tooltip
// const tooltip = d3.select('#chart-container').append('div').attr('class', 'tooltip');
@@ -67,41 +64,19 @@ export class LineChart extends React.Component<LineChartProps> {
return;
}
// adding x axis
- const xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.props.width]);
-
- const yScale = d3.scaleLinear().domain([0, yMax]).range([this.props.height, 0]);
+ const xScale = scaleCreator(xMin, xMax, 0, this.props.width);
+ const yScale = scaleCreator(0, yMax, this.props.height, 0);
// create a line function that takes in the data.data.x and data.data.y
// TODO: nda - fix the types for the d here
- const lineGen = d3
- .line<DataPoints>()
- .x(d => xScale(d.x))
- .y(d => yScale(d.y))
- .curve(d3.curveMonotoneX);
-
- // adding the line to the svg
- svg.append('g')
- .attr('class', 'grid')
- .attr('transform', `translate(0,${this.props.height})`)
- .call(
- d3
- .axisBottom(xScale)
- .tickSize(-this.props.height)
- .tickFormat((a, b) => '')
- );
- svg.append('g')
- .attr('class', 'grid')
- .call(
- d3
- .axisLeft(yScale)
- .tickSize(-this.props.width)
- .tickFormat((a, b) => '')
- );
- svg.append('g').attr('class', 'x-axis').attr('transform', `translate(0,${this.props.height})`).call(d3.axisBottom(xScale).tickSize(15));
- svg.append('g').attr('class', 'y-axis').call(d3.axisLeft(yScale));
+ const lineGen = createLineGenerator(xScale, yScale);
+
+ // create x and y grids
+ xGrid(svg.append('g'), this.props.height, xScale);
+ yGrid(svg.append('g'), this.props.width, yScale);
// draw the line
- svg.append('path').datum(data.data).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('class', 'line').attr('d', lineGen);
+ drawLine(svg.append('path'), data.data, lineGen);
// draw the datapoint circles
svg.selectAll('.circle-d1')
@@ -132,7 +107,7 @@ export class LineChart extends React.Component<LineChartProps> {
// add all the tooltipContent to the tooltip
// @action
const mousemove = action((e: any) => {
- const bisect = d3.bisector((d: DataPoints) => d.x).left;
+ const bisect = d3.bisector((d: DataPoint) => d.x).left;
const xPos = d3.pointer(e)[0];
const x0 = bisect(data.data, xScale.invert(xPos));
const d0 = data.data[x0];
@@ -145,7 +120,7 @@ export class LineChart extends React.Component<LineChartProps> {
});
const onPointClick = action((e: any) => {
- const bisect = d3.bisector((d: DataPoints) => d.x).left;
+ const bisect = d3.bisector((d: DataPoint) => d.x).left;
const xPos = d3.pointer(e)[0];
const x0 = bisect(data.data, xScale.invert(xPos));
const d0 = data.data[x0];
@@ -175,7 +150,7 @@ export class LineChart extends React.Component<LineChartProps> {
render() {
return (
- <div id="chart-container">
+ <div ref={this._chartRef} className="chart-container">
<span>
x: {this._x} y: {this._y}
Curr Selected: {this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'}
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
new file mode 100644
index 000000000..285298472
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -0,0 +1,59 @@
+import d3 from 'd3';
+import { DataPoint } from '../ChartBox';
+
+// TODO: nda - implement function that can handle range for strings
+
+export const minMaxRange = (dataPts: DataPoint[]) => {
+ const yMin = d3.min(dataPts, d => d.y);
+ const yMax = d3.max(dataPts, d => d.y);
+
+ const xMin = d3.min(dataPts, d => d.x);
+ const xMax = d3.max(dataPts, d => d.x);
+
+ return { xMin, xMax, yMin, yMax };
+};
+
+export const scaleCreator = (domA: number, domB: number, rangeA: number, rangeB: number) => {
+ return d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]);
+};
+
+export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) => {
+ // TODO: nda - look into the different types of curves
+ return d3
+ .line<DataPoint>()
+ .x(d => xScale(d.x))
+ .y(d => yScale(d.y))
+ .curve(d3.curveMonotoneX);
+};
+
+export const xAxisCreator = (g: d3.Selection<SVGGElement, unknown, HTMLElement, any>, height: number, xScale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15));
+};
+
+export const yAxisCreator = (g: d3.Selection<SVGGElement, unknown, HTMLElement, any>, width: number, yScale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'y-axis').call(d3.axisLeft(yScale));
+};
+
+export const xGrid = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, scale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'grid')
+ .attr('transform', `translate(0,${height})`)
+ .call(
+ d3
+ .axisBottom(scale)
+ .tickSize(-height)
+ .tickFormat((a, b) => '')
+ );
+};
+
+export const yGrid = (g: d3.Selection<SVGGElement, unknown, null, undefined>, width: number, scale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'grid').call(
+ d3
+ .axisLeft(scale)
+ .tickSize(-width)
+ .tickFormat((a, b) => '')
+ );
+};
+
+export const drawLine = (p: d3.Selection<SVGPathElement, unknown, null, undefined>, dataPts: DataPoint[], lineGen: d3.Line<DataPoint>) => {
+ p.datum(dataPts).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('class', 'line').attr('d', lineGen);
+};