Merge pull request #80 from chanzuckerberg/#62-persist-scatter

render scatterplot on mount, handle switch tabs case
This commit is contained in:
Colin Megill
2018-06-06 17:43:47 -04:00
committed by GitHub
+66 -46
View File
@@ -51,6 +51,7 @@ class Scatterplot extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.count = 0; this.count = 0;
this.axes = false;
this.state = { this.state = {
svg: null, svg: null,
// ctx: null, // ctx: null,
@@ -63,8 +64,27 @@ class Scatterplot extends React.Component {
componentDidMount() { componentDidMount() {
const { svg } = setupScatterplot(width, height, margin); const { svg } = setupScatterplot(width, height, margin);
let scales;
/* if we've already got the data, user clicked back and forth between tabs, so render the scatterplot */
if (
this.props.expression &&
this.props.expression.data &&
this.props.scatterplotXXaccessor &&
this.props.scatterplotYYaccessor
) {
scales = this.setupScales(
this.props.expression,
this.props.scatterplotXXaccessor,
this.props.scatterplotYYaccessor
);
this.drawAxesSVG(scales.xScale, scales.yScale, svg);
}
this.setState({ this.setState({
svg svg,
xScale: scales ? scales.xScale : null,
yScale: scales ? scales.yScale : null
}); });
const camera = _camera(this.reglCanvas, { scale: true, rotate: false }); const camera = _camera(this.reglCanvas, { scale: true, rotate: false });
@@ -104,21 +124,36 @@ class Scatterplot extends React.Component {
}); });
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
this.maybeSetupScalesAndDrawAxes(nextProps); if (
nextProps.expression &&
nextProps.expression.data &&
nextProps.scatterplotXXaccessor &&
nextProps.scatterplotYYaccessor
) {
const scales = this.setupScales(
nextProps.expression,
nextProps.scatterplotXXaccessor,
nextProps.scatterplotYYaccessor
);
this.setState(scales);
}
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if ( if (
this.state.svg &&
this.state.xScale && this.state.xScale &&
this.state.yScale && this.state.yScale &&
this.props.scatterplotXXaccessor && this.props.scatterplotXXaccessor &&
this.props.scatterplotYYaccessor && this.props.scatterplotYYaccessor &&
(this.props.scatterplotXXaccessor !== prevProps.scatterplotXXaccessor || // was CLU now FTH1 etc (this.props.scatterplotXXaccessor !== prevProps.scatterplotXXaccessor || // was CLU now FTH1 etc
this.props.scatterplotYYaccessor !== prevProps.scatterplotYYaccessor) this.props.scatterplotYYaccessor !== prevProps.scatterplotYYaccessor || // was CLU now FTH1 etc
!this.axes) // clicked off the tab and back again, rerender
) { ) {
this.drawAxesSVG(this.state.xScale, this.state.yScale); this.drawAxesSVG(this.state.xScale, this.state.yScale, this.state.svg);
} }
if ( if (
this.props.metadata &&
this.state.regl && this.state.regl &&
this.state.pointBuffer && this.state.pointBuffer &&
this.state.colorBuffer && this.state.colorBuffer &&
@@ -179,47 +214,32 @@ class Scatterplot extends React.Component {
this.count = cellCount; this.count = cellCount;
} }
} }
maybeSetupScalesAndDrawAxes(nextProps) { setupScales(expression, scatterplotXXaccessor, scatterplotYYaccessor) {
if ( const xScale = d3
nextProps.expression && .scaleLinear()
nextProps.expression.data && .domain(
nextProps.scatterplotXXaccessor && d3.extent(expression.data.cells, (cell, i) => {
nextProps.scatterplotYYaccessor return cell.e[expression.data.genes.indexOf(scatterplotXXaccessor)];
) { })
const xScale = d3 )
.scaleLinear() .range([0, width]);
.domain(
d3.extent(nextProps.expression.data.cells, (cell, i) => {
return cell.e[
nextProps.expression.data.genes.indexOf(
nextProps.scatterplotXXaccessor
)
];
})
)
.range([0, width]);
const yScale = d3 const yScale = d3
.scaleLinear() .scaleLinear()
.domain( .domain(
d3.extent(nextProps.expression.data.cells, cell => { d3.extent(expression.data.cells, cell => {
return cell.e[ return cell.e[expression.data.genes.indexOf(scatterplotYYaccessor)];
nextProps.expression.data.genes.indexOf( })
nextProps.scatterplotYYaccessor )
) .range([height, 0]);
];
})
)
.range([height, 0]);
this.setState({ return {
xScale, xScale,
yScale yScale
}); };
}
} }
drawAxesSVG(xScale, yScale) { drawAxesSVG(xScale, yScale, svg) {
this.state.svg.selectAll("*").remove(); svg.selectAll("*").remove();
// the axes are much cleaner and easier now. No need to rotate and orient the axis, just call axisBottom, axisLeft etc. // the axes are much cleaner and easier now. No need to rotate and orient the axis, just call axisBottom, axisLeft etc.
var xAxis = d3.axisBottom().scale(xScale); var xAxis = d3.axisBottom().scale(xScale);
@@ -227,28 +247,28 @@ class Scatterplot extends React.Component {
var yAxis = d3.axisLeft().scale(yScale); var yAxis = d3.axisLeft().scale(yScale);
// adding axes is also simpler now, just translate x-axis to (0,height) and it's alread defined to be a bottom axis. // adding axes is also simpler now, just translate x-axis to (0,height) and it's alread defined to be a bottom axis.
this.state.svg svg
.append("g") .append("g")
.attr("transform", "translate(0," + height + ")") .attr("transform", "translate(0," + height + ")")
.attr("class", "x axis") .attr("class", "x axis")
.call(xAxis); .call(xAxis);
// y-axis is translated to (0,0) // y-axis is translated to (0,0)
this.state.svg svg
.append("g") .append("g")
.attr("transform", "translate(0,0)") .attr("transform", "translate(0,0)")
.attr("class", "y axis") .attr("class", "y axis")
.call(yAxis); .call(yAxis);
// adding label. For x-axis, it's at (10, 10), and for y-axis at (width, height-10). // adding label. For x-axis, it's at (10, 10), and for y-axis at (width, height-10).
this.state.svg svg
.append("text") .append("text")
.attr("x", 10) .attr("x", 10)
.attr("y", 10) .attr("y", 10)
.attr("class", "label") .attr("class", "label")
.text(this.props.scatterplotYYaccessor); .text(this.props.scatterplotYYaccessor);
this.state.svg svg
.append("text") .append("text")
.attr("x", width) .attr("x", width)
.attr("y", height - 10) .attr("y", height - 10)