Skip to content

Commit b8d3b1f

Browse files
committed
Make sunburst compatible with React 18 rendering mode
1 parent 50b3fbc commit b8d3b1f

2 files changed

Lines changed: 56 additions & 65 deletions

File tree

Lines changed: 40 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,79 @@
1-
// Display a d3 Sunburst diagram
21
import PropTypes from 'prop-types';
3-
import React from 'react';
2+
import React, { useEffect, useRef, useState } from 'react';
43
import round from 'lodash/round';
54
import { select } from 'd3-selection';
65
import { hierarchy, partition } from 'd3-hierarchy';
76
import { arc } from 'd3-shape';
87
import { getSize } from './sunburst-container-utils.js';
98

10-
function getDistance (capture) {
9+
function getDistance(capture) {
1110
if (capture.similarity !== -1) {
1211
return `${capture.name} - Differences: ${round(capture.similarity, 2)}%`;
1312
}
1413
}
1514

16-
export default class D3Sunburst extends React.Component {
17-
static propTypes = {
18-
simhashData: PropTypes.object,
19-
url: PropTypes.string,
20-
urlPrefix: PropTypes.string
21-
};
15+
export default function D3Sunburst({ simhashData, urlPrefix, url }) {
16+
const chartRef = useRef(null);
17+
const [hint, setHint] = useState('');
2218

23-
state = {
24-
hint: ''
25-
};
26-
27-
constructor (props) {
28-
super(props);
29-
this.chartRef = React.createRef();
30-
}
31-
32-
componentDidMount () {
33-
const { simhashData, urlPrefix, url } = this.props;
19+
useEffect(() => {
3420
const width = getSize();
3521
const height = getSize();
3622
const radius = Math.min(width, height) / 2;
3723

38-
// Create primary <g> element
39-
const g = select(this.chartRef.current)
24+
const svg = select(chartRef.current)
4025
.attr('width', width)
41-
.attr('height', height)
42-
.append('g')
43-
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
26+
.attr('height', height);
27+
28+
const g = svg.append('g')
29+
.attr('transform', `translate(${width / 2},${height / 2})`);
4430

45-
// Data strucure
4631
const structure = partition().size([2 * Math.PI, radius]);
4732

48-
// Find data root
49-
const root = hierarchy(simhashData).sum(function (d) { return 5; });
50-
// Size arcs
33+
const root = hierarchy(simhashData).sum(() => 5);
5134
structure(root);
52-
const myarc = arc()
53-
.startAngle(function (d) { return d.x0; })
54-
.endAngle(function (d) { return d.x1; })
55-
.innerRadius(function (d) { return d.y0; })
56-
.outerRadius(function (d) { return d.y1; });
57-
const component = this;
5835

59-
// Put it all together
36+
const myArc = arc()
37+
.startAngle(d => d.x0)
38+
.endAngle(d => d.x1)
39+
.innerRadius(d => d.y0)
40+
.outerRadius(d => d.y1);
41+
6042
g.selectAll('path')
6143
.data(root.descendants())
6244
.enter().append('path')
63-
.attr('display', function (d) { return d.depth ? null : 'none'; })
64-
.attr('d', myarc)
45+
.attr('display', d => (d.depth ? null : 'none'))
46+
.attr('d', myArc)
6547
.style('stroke', '#fff')
66-
.style('fill', function (d) { return d.data.clr; })
48+
.style('fill', d => d.data.clr)
6749
.on('mouseover', function (e, d) {
6850
select(e.currentTarget).style('cursor', 'pointer').style('stroke', 'black');
69-
component.setState({ hint: getDistance(d.data) });
51+
setHint(getDistance(d.data));
7052
})
71-
.on('mouseleave', function (e, d) {
53+
.on('mouseleave', function (e) {
7254
select(e.currentTarget).style('cursor', 'default').style('stroke', '');
73-
component.setState({ hint: '' });
55+
setHint('');
7456
})
7557
.on('click', function (e, d) {
7658
if (d.data.timestamp !== simhashData.timestamp) {
77-
const captureUrl = urlPrefix + d.data.timestamp + '/' + simhashData.timestamp + '/' + url;
59+
const captureUrl = `${urlPrefix}${d.data.timestamp}/${simhashData.timestamp}/${url}`;
7860
window.open(captureUrl, '_blank');
7961
}
8062
});
81-
}
63+
}, [simhashData, urlPrefix, url]);
8264

83-
render () {
84-
return (
85-
<>
86-
<p className="text-center" style={{ marginTop: '10px' }}>{ this.state.hint } &nbsp;</p>
87-
<div className="text-center">
88-
<svg ref={this.chartRef}></svg>
89-
</div>
90-
</>
91-
);
92-
}
65+
return (
66+
<>
67+
<p className="text-center" style={{ marginTop: '10px' }}>{hint}&nbsp;</p>
68+
<div className="text-center">
69+
<svg ref={chartRef}></svg>
70+
</div>
71+
</>
72+
);
9373
}
74+
75+
D3Sunburst.propTypes = {
76+
simhashData: PropTypes.object,
77+
url: PropTypes.string,
78+
urlPrefix: PropTypes.string
79+
};

src/components/sunburst/sunburst-container.jsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import D3Sunburst from './d3-sunburst.jsx';
33
import scaleCluster from 'd3-scale-cluster';
44
import '../../css/diffgraph.css';
5-
import { similarityWithDistance, checkResponse, fetchWithTimeout, getUTCDateFormat }
5+
import { similarityWithDistance, checkResponse, getUTCDateFormat }
66
from '../../js/utils.js';
77
import { decodeCompressedJson, decodeUncompressedJson } from './sunburst-container-utils.js';
88
import ErrorMessage from '../errors.jsx';
@@ -47,15 +47,25 @@ export default class SunburstContainer extends React.Component {
4747
constructor (props) {
4848
super(props);
4949
this.state = {
50+
timestamp: this.props.timestamp,
5051
isPending: false,
5152
simhashData: null
5253
};
5354
this._clusters = [];
5455
}
5556

57+
componentDidMount () {
58+
if (this.state.timestamp) {
59+
this._fetchTimestampSimhashData();
60+
} else {
61+
this._validateTimestamp();
62+
}
63+
}
64+
5665
render () {
5766
const { url, conf, loader } = this.props;
5867
const { countCaptures, error, simhashData, timestamp } = this.state;
68+
const Loader = () => isNil(loader) ? <Loading/> : loader;
5969

6070
if (error) {
6171
return (
@@ -80,13 +90,6 @@ export default class SunburstContainer extends React.Component {
8090
</div>
8191
);
8292
}
83-
// TODO Must move this outside here.
84-
const Loader = () => isNil(loader) ? <Loading/> : loader;
85-
if (timestamp) {
86-
this._fetchTimestampSimhashData();
87-
} else {
88-
this._validateTimestamp();
89-
}
9093
return <div className="loading"><Loader/></div>;
9194
}
9295

@@ -100,7 +103,7 @@ export default class SunburstContainer extends React.Component {
100103
reqUrl.searchParams.append('limit', '1');
101104
reqUrl.searchParams.append('fl', 'timestamp');
102105

103-
fetchWithTimeout(reqUrl)
106+
fetch(reqUrl)
104107
.then(checkResponse)
105108
.then(response => response.json())
106109
.then(data => {
@@ -124,10 +127,11 @@ export default class SunburstContainer extends React.Component {
124127
}
125128

126129
_fetchTimestampSimhashData () {
130+
console.log('_fetchTimestampSimhashData');
127131
const { url, conf } = this.props;
128132
const { timestamp } = this.state;
129133
const fetchUrl = conf.waybackDiscoverDiff + '/simhash?url=' + encodeURIComponent(url) + '&timestamp=' + timestamp;
130-
fetchWithTimeout(fetchUrl).then(checkResponse)
134+
fetch(fetchUrl).then(checkResponse)
131135
.then(response => response.json())
132136
.then((jsonResponse) => {
133137
if (jsonResponse.status) {
@@ -141,13 +145,14 @@ export default class SunburstContainer extends React.Component {
141145
}
142146

143147
_fetchSimhashData (timestampJson) {
148+
console.log('_fetchSimhashData');
144149
const { url, conf } = this.props;
145150
const { timestamp } = this.state;
146151
let fetchUrl = conf.waybackDiscoverDiff + '/simhash?url=' + encodeURIComponent(url) + '&year=' + timestamp.substring(0, 4);
147152
if (conf.compressedSimhash) {
148153
fetchUrl += '&compress=1';
149154
}
150-
fetchWithTimeout(fetchUrl).then(checkResponse)
155+
fetch(fetchUrl).then(checkResponse)
151156
.then(response => response.json())
152157
.then((jsonResponse) => {
153158
this.setState({ isPending: jsonResponse.status === 'PENDING' });

0 commit comments

Comments
 (0)