Skip to content

Commit 4bd126a

Browse files
authored
Merge pull request #43 from oslabs-beta/staging
Staging
2 parents 7652bdf + 11f8f4c commit 4bd126a

File tree

4 files changed

+114
-100
lines changed

4 files changed

+114
-100
lines changed

src/app/components/ErrorHandler.jsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
3+
class ErrorHandler extends React.Component {
4+
constructor(props) {
5+
super(props);
6+
this.state = { errorOccurred: false };
7+
}
8+
9+
componentDidCatch(error, info) {
10+
this.setState({ errorOccurred: true })
11+
console.log('Error occurred in React Component: ', error, info);
12+
}
13+
14+
render() {
15+
return this.state.errorOccurred ? <h1>An error occurred</h1> : this.props.children
16+
}
17+
}
18+
19+
export default ErrorHandler;

src/app/components/PerfView.jsx

Lines changed: 74 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-use-before-define */
12
/* eslint-disable react/no-this-in-sfc */
23
/* eslint-disable no-unused-vars */
34
/* eslint-disable react/prop-types */
@@ -17,151 +18,130 @@
1718

1819
import React, { useEffect, useState, useRef, useCallback } from 'react';
1920
import * as d3 from 'd3';
20-
// import { addNewSnapshots } from '../actions/actions';
21+
import { schemeSet1 as colorScheme } from 'd3';
2122

22-
// const windowRef = useRef(null);
23-
// const winWidth = null;
24-
// const winHeight = null;
23+
// import { addNewSnapshots } from '../actions/actions';
2524

26-
// useEffect(() => {
27-
// if (windowRef.current) {
28-
// winWidth = windowRef.current.offsetHeight;
29-
// winHeight = windowRef.current.offsetWidth;
30-
// console.log('** SETTING WINDOW SIZES: ', winWidth, winHeight);
31-
// }
32-
// }, [windowRef]);
3325

34-
const PerfView = ({ snapshots, viewIndex }) => {
35-
const [chartData, updateChartData] = useState(snapshots[snapshots.length - 1]);
26+
const PerfView = ({ snapshots, viewIndex, width = 600, height = 600 }) => {
27+
// console.log('***** constructor *****');
3628
const svgRef = useRef(null);
3729

38-
// Todo: implement update functions...
39-
const [curZoom, setZoom] = useState(null);
40-
const [width, setWidth] = useState(600);
41-
const [height, setHeight] = useState(600);
30+
// Figure out which snapshot index to use
31+
let indexToDisplay = null;
32+
if (viewIndex < 0) indexToDisplay = snapshots.length - 1;
33+
else indexToDisplay = viewIndex;
4234

43-
// set up color scaling function
44-
const color = d3.scaleLinear()
35+
// Set up color scaling function
36+
const colorScale = d3.scaleLinear()
4537
.domain([0, 7])
46-
.range(['hsl(152,80%,80%)', 'hsl(228,30%,40%)'])
38+
.range(['hsl(200,60%,60%)', 'hsl(255,30%,30%)'])
4739
.interpolate(d3.interpolateHcl);
4840

49-
// set up circle-packing layout function
41+
// Set up circle-packing layout function
5042
const packFunc = useCallback(data => {
5143
return d3.pack()
5244
.size([width, height])
5345
.padding(3)(d3.hierarchy(data)
54-
.sum(d => {
55-
// console.log('in pack func. d=', d);
56-
return d.componentData.actualDuration;
57-
})
58-
.sort((a, b) => {
59-
// console.log('in sort func. a&b=', a, b);
60-
return b.value - a.value;
61-
}));
46+
.sum(d => { return d.componentData.actualDuration || 0; })
47+
.sort((a, b) => { return b.value - a.value; }));
6248
}, [width, height]);
6349

64-
// first run, or user has made changes in their app; clear old tree and get current chartData
50+
// If indexToDisplay changes, clear old tree nodes
6551
useEffect(() => {
66-
console.log('PerfView -> snapshots', snapshots);
67-
console.log('Current viewIndex: ', viewIndex);
68-
for (let i = 0; i < snapshots.length; i++) {
69-
console.log(`SNAPSHOT[${i}] App actualDuration:`, snapshots[i].children[0].componentData.actualDuration);
70-
}
71-
72-
// clear old tree
52+
// console.log('***** useEffect - CLEARING');
7353
while (svgRef.current.hasChildNodes()) {
7454
svgRef.current.removeChild(svgRef.current.lastChild);
7555
}
76-
77-
let indexToDisplay = null;
78-
if (viewIndex < 0) indexToDisplay = snapshots.length - 1;
79-
else indexToDisplay = viewIndex;
80-
81-
updateChartData(snapshots[indexToDisplay]);
82-
console.log(`Using snapshots[${indexToDisplay}]`);
83-
}, [svgRef, viewIndex, snapshots, chartData]);
56+
}, [indexToDisplay, svgRef]);
8457

8558
useEffect(() => {
86-
console.log('PerfView -> chartData', chartData);
59+
// console.log(`***** useEffect - MAIN -> snapshots[${indexToDisplay}]`, snapshots[indexToDisplay]);
60+
61+
// Error, no App-level component present
62+
if (snapshots[indexToDisplay].children.length < 1) return;
8763

88-
// generate tree with our data
89-
const packedRoot = packFunc(chartData);
90-
// console.log('PerfView -> packedRoot', packedRoot);
64+
// Generate tree with our data
65+
const packedRoot = packFunc(snapshots[indexToDisplay]);
9166

92-
// initial focus points at root of tree
93-
let focus = packedRoot;
67+
// Set initial focus to root node
68+
let curFocus = packedRoot;
69+
70+
// View [x, y, r]
9471
let view;
9572

96-
// set up viewBox dimensions and onClick for parent svg
73+
// Set up viewBox dimensions and onClick for parent svg
9774
const svg = d3.select(svgRef.current)
9875
.attr('viewBox', `-${width / 2} -${height / 2} ${width} ${height}`)
99-
.on('click', () => zoom(packedRoot));
76+
.on('click', () => zoomToNode(packedRoot));
10077

101-
// connect circles below root to data
78+
// Connect circles below root to data
10279
const node = svg.append('g')
10380
.selectAll('circle')
10481
.data(packedRoot.descendants().slice(1))
10582
.enter().append('circle')
106-
.attr('fill', d => (d.children ? color(d.depth) : 'white'))
107-
.attr('pointer-events', d => (!d.children ? 'none' : null))
108-
.on('mouseover', () => d3.select(this).attr('stroke', '#000'))
109-
.on('mouseout', () => d3.select(this).attr('stroke', null))
110-
.on('click', d => focus !== d && (zoom(d), d3.event.stopPropagation()));
111-
112-
// console.log('PerfView -> node', node);
113-
// console.log('packedRoot.descendants()', packedRoot.descendants());
83+
.attr('fill', d => (d.children ? colorScale(d.depth) : 'white'))
84+
.attr('pointer-events', d => (!d.children ? 'none' : null))
85+
.on('mouseover', () => d3.select(this).attr('stroke', '#000'))
86+
.on('mouseout', () => d3.select(this).attr('stroke', null))
87+
.on('click', d => curFocus !== d && (zoomToNode(d), d3.event.stopPropagation()));
11488

115-
// generate text labels
89+
// Generate text labels. Set (only) root to visible initially
11690
const label = svg.append('g')
117-
.attr('class', 'perf-chart-labels')
91+
// .style('fill', 'rgb(231, 231, 231)')
92+
.attr('class', 'perf-chart-labels')
11893
.selectAll('text')
11994
.data(packedRoot.descendants())
12095
.enter().append('text')
121-
.style('fill-opacity', d => (d.parent === packedRoot ? 1 : 0))
122-
.style('display', d => (d.parent === packedRoot ? 'inline' : 'none'))
123-
.text(d => {
124-
// console.log('generating text label for d: ', d);
125-
return `${d.data.name}: ${Number.parseFloat(d.data.componentData.actualDuration).toFixed(2)}ms`;
126-
});
96+
.style('fill-opacity', d => (d.parent === packedRoot ? 1 : 0))
97+
.style('display', d => (d.parent === packedRoot ? 'inline' : 'none'))
98+
.text(d => `${d.data.name}: \
99+
${Number.parseFloat(d.data.componentData.actualDuration || 0).toFixed(2)}ms`);
127100

101+
// Remove any unused nodes
128102
label.exit().remove();
129103
node.exit().remove();
130104

131-
// jump to default zoom state
132-
zoomTo([packedRoot.x, packedRoot.y, packedRoot.r * 2]);
105+
// Zoom size of nodes and labels to focus view on root node
106+
zoomViewArea([packedRoot.x, packedRoot.y, packedRoot.r * 2]);
133107

134-
function zoomTo(v) {
135-
// console.log("zoomTo -> v", v);
136-
const k = width / v[2];
137-
view = v;
138-
label.attr('transform', d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
139-
node.attr('transform', d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
108+
// Zoom/relocated nodes and labels based on dimensions given [x, y, r]
109+
function zoomViewArea(newXYR) {
110+
console.log("zoomTo -> newXYR", newXYR);
111+
const k = width / newXYR[2];
112+
view = newXYR;
113+
label.attr('transform', d => `translate(${(d.x - newXYR[0]) * k},${(d.y - newXYR[1]) * k})`);
114+
node.attr('transform', d => `translate(${(d.x - newXYR[0]) * k},${(d.y - newXYR[1]) * k})`);
140115
node.attr('r', d => d.r * k);
141116
}
142117

143-
function zoom(d) {
118+
// Transition visibility of labels
119+
function zoomToNode(newFocus) {
144120
// console.log("zoom -> d", d);
145-
const focus0 = focus;
146-
focus = d;
147-
148121
const transition = svg.transition()
149-
.duration(d3.event.altKey ? 7500 : 750)
150-
.tween('zoom', d => {
151-
const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
152-
return t => zoomTo(i(t));
153-
});
154-
155-
label
156-
.filter(function (d) { return d.parent === focus || this.style.display === 'inline'; })
122+
.duration(d3.event.altKey ? 7500 : 750)
123+
.tween('zoom', d => {
124+
const i = d3.interpolateZoom(view, [newFocus.x, newFocus.y, newFocus.r * 2]);
125+
return t => zoomViewArea(i(t));
126+
});
127+
128+
// Grab all nodes that were previously displayed, or who's parent is the new target newFocus
129+
// Transition their labels to visible or not
130+
label.filter(function (d) { console.log('label filtering. d=', d); return d.parent === newFocus || this.style.display === 'inline'; })
157131
.transition(transition)
158-
.style('fill-opacity', d => (d.parent === focus ? 1 : 0))
159-
.on('start', function (d) { if (d.parent === focus) this.style.display = 'inline'; })
160-
.on('end', function (d) { if (d.parent !== focus) this.style.display = 'none'; });
132+
.style('fill-opacity', d => (d.parent === newFocus ? 1 : 0))
133+
.on('start', function (d) { if (d.parent === newFocus) this.style.display = 'inline'; })
134+
.on('end', function (d) { if (d.parent !== newFocus) this.style.display = 'none'; });
135+
136+
curFocus = newFocus;
161137
}
162-
}, [chartData, color, packFunc, width, height]);
138+
}, [colorScale, packFunc, width, height, indexToDisplay, snapshots]);
163139

164140
return <svg className="perfContainer" ref={svgRef} />;
165141
};
166142

167143
export default PerfView;
144+
145+
146+
// d3.quantize(d3.interpolateHcl('#60c96e', '#4d4193'), 10);
147+
// const colorScale = d3.scaleOrdinal(colorScheme);

src/app/components/StateRoute.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { MemoryRouter as Router, Route, NavLink, Switch } from 'react-router-dom
77
import Chart from './Chart';
88
import Tree from './Tree';
99
import PerfView from './PerfView';
10+
import ErrorHandler from './ErrorHandler';
1011

1112
const NO_STATE_MSG = 'No state change detected. Trigger an event to change state';
1213
// eslint-disable-next-line react/prop-types
@@ -30,7 +31,11 @@ const StateRoute = ({ snapshot, hierarchy, snapshots, viewIndex }) => {
3031

3132
const renderPerfView = () => {
3233
if (hierarchy) {
33-
return <PerfView viewIndex={viewIndex} snapshots={snapshots} />; // ref={windowRef}
34+
return (
35+
<ErrorHandler>
36+
<PerfView viewIndex={viewIndex} snapshots={snapshots} />
37+
</ErrorHandler>
38+
);
3439
}
3540
return <div className="noState">{NO_STATE_MSG}</div>;
3641
};
@@ -65,3 +70,9 @@ StateRoute.propTypes = {
6570
};
6671

6772
export default StateRoute;
73+
74+
// <div>
75+
// <PerfView viewIndex={viewIndex} snapshots={snapshots} />
76+
// <div className="ancestorStatus">Test</div>
77+
// </div>
78+
// );

src/app/styles/components/d3graph.css

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,21 @@ div.tooltip {
9595

9696
.perfContainer {
9797
display: block;
98-
margin: 0 -14px;
99-
background-color: hsl(221,16%,20%);
98+
/* margin: 0 -14px;
99+
background-color: hsl(152,80%,80%); */
100100
/* border: 2px solid red; */
101101
}
102102

103103
.d3divContainer {
104-
/* border: 1px solid blue; */
104+
/* display: flex;
105+
flex-direction: column;
106+
justify-content: space-between; */
105107
}
106108

107109
.perf-chart-labels {
108-
font: 11px sans-serif;
110+
font: 15px sans-serif;
111+
color: white;
109112
pointer-events: none;
110113
text-anchor: middle;
111-
};
114+
};
115+

0 commit comments

Comments
 (0)