Updating to latest cellxgene client code

Fixes the build error in the old version. 

Reverted the API version since we changed directions from updating the API to refactoring the server structure instead.
This commit is contained in:
Charlotte Weaver
2018-07-05 13:37:26 -07:00
parent 2da62720dd
commit 85506db971
18 changed files with 845 additions and 147 deletions
@@ -15,7 +15,8 @@ import FaPaintBrush from "react-icons/lib/fa/paint-brush";
@connect(state => {
return {
colorAccessor: state.controls.colorAccessor
colorAccessor: state.controls.colorAccessor,
categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap
};
})
class Category extends React.Component {
@@ -26,7 +27,24 @@ class Category extends React.Component {
isExpanded: false
};
}
componentDidUpdate() {
const valuesAsBool = _.values(
this.props.categoricalAsBooleansMap[this.props.metadataField]
)
/* count categories toggled on by counting true values */
const categoriesToggledOn = _.values(valuesAsBool).filter(v => v).length;
if (categoriesToggledOn === valuesAsBool.length) {
/* everything is on, so not indeterminate */
this.checkbox.indeterminate = false;
} else if (categoriesToggledOn === 0) {
/* nothing is on, so no */
this.checkbox.indeterminate = false;
} else if (categoriesToggledOn < valuesAsBool.length) {
/* to be explicit... */
this.checkbox.indeterminate = true;
}
}
handleColorChange() {
this.props.dispatch({
type: "color by categorical metadata",
@@ -61,6 +79,16 @@ class Category extends React.Component {
);
});
}
handleToggleAllClick() {
// || this.checkbox.indeterminate === false
if (this.state.isChecked) {
console.log('checked, firing toggle none')
this.toggleNone();
} else if (!this.state.isChecked) {
console.log('!checked, firing toggle all')
this.toggleAll()
}
}
render() {
return (
<div
@@ -102,11 +130,8 @@ class Category extends React.Component {
</span>
{this.props.metadataField}
<input
onChange={
this.state.isChecked
? this.toggleNone.bind(this)
: this.toggleAll.bind(this)
}
onChange={this.handleToggleAllClick.bind(this)}
ref={el => this.checkbox = el}
checked={this.state.isChecked}
type="checkbox"
/>
@@ -7,8 +7,11 @@ https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import FaPaintBrush from "react-icons/lib/fa/paint-brush";
import * as globals from "../../globals";
@connect(state => {
console.log("state in histo brush", state)
const ranges =
state.cells.cells && state.cells.cells.data.ranges
? state.cells.cells.data.ranges
@@ -26,7 +29,8 @@ import { connect } from "react-redux";
return {
colorAccessor: state.controls.colorAccessor,
colorScale: state.controls.colorScale,
cellsMetadata: state.controls.cellsMetadata
cellsMetadata: state.controls.cellsMetadata,
initializeRanges
};
})
class HistogramBrush extends React.Component {
@@ -154,7 +158,7 @@ class HistogramBrush extends React.Component {
)
.call(d3.axisBottom(x).ticks(5))
.append("text")
.attr("x", 300)
.attr("x", this.width - 2)
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "end")
@@ -164,13 +168,40 @@ class HistogramBrush extends React.Component {
this.setState({ brush, xAxis });
}
}
handleColorAction() {
this.props.dispatch({
type: "color by continuous metadata",
colorAccessor: this.props.metadataField,
rangeMaxForColorAccessor: this.props.initializeRanges[this.props.metadataField].range.max
});
}
render() {
return (
<div
style={{ marginTop: 10 }}
style={{ marginTop: 10, position: "relative" }}
id={"histogram_" + this.props.metadataField}
>
<span
onClick={this.handleColorAction.bind(this)}
style={{
fontSize: 16,
marginLeft: 4,
// padding: this.props.colorAccessor === this.props.metadataField ? 3 : "auto",
borderRadius: 3,
color:
this.props.colorAccessor === this.props.metadataField
? globals.brightBlue
: "black",
// backgroundColor: this.props.colorAccessor === this.props.metadataField ? globals.brightBlue : "inherit",
display: "inline-block",
position: "relative",
left: this.width,
top: this.height - 22,
cursor: "pointer"
}}
>
<FaPaintBrush />
</span>
<svg
width={this.width}
height={this.height}
@@ -103,7 +103,11 @@ class ContinuousLegend extends React.Component {
this.state = {};
}
componentWillReceiveProps(nextProps) {
if (nextProps.colorAccessor !== this.props.colorAccessor) {
if (
nextProps.colorAccessor !== this.props.colorAccessor ||
nextProps.responsive.height !== this.props.responsive.height ||
nextProps.responsive.width !== this.props.responsive.width
) {
/* always remove it, if it's not continuous we don't put it back. */
d3
.select("#continuous_legend")
+45 -29
View File
@@ -164,28 +164,29 @@ class Graph extends React.Component {
nextProps.responsive.width !== this.props.responsive.width
) {
/* clear out whatever was on the div, even if nothing, but usually the brushes etc */
d3
.select("#graphAttachPoint")
d3.select("#graphAttachPoint")
.selectAll("*")
.remove();
const { svg } = setupSVGandBrushElements(
const { svg, brush, brushContainer } = setupSVGandBrushElements(
this.handleBrushSelectAction.bind(this),
this.handleBrushDeselectAction.bind(this),
nextProps.responsive,
this.graphPaddingTop
);
this.setState({ svg });
this.setState({ svg, brush, brushContainer });
}
}
handleBrushSelectAction() {
/*
/* This conditional handles procedural brush deselect. Brush emits an event on procedural deselect because it is move: null */
if (d3.event.sourceEvent !== null) {
/*
No idea why d3 event scope works like this
but apparently
it does
https://bl.ocks.org/EfratVil/0e542f5fc426065dd1d4b6daaa345a9f
*/
const s = d3.event.selection;
/*
const s = d3.event.selection;
/*
event describing brush position:
@-------|
| |
@@ -193,33 +194,47 @@ class Graph extends React.Component {
|-------@
*/
// compute inverse view matrix
const inverse = mat4.invert([], this.state.camera.view());
// compute inverse view matrix
const inverse = mat4.invert([], this.state.camera.view());
// transform screen coordinates -> cell coordinates
const invert = pin => {
const x =
2 * pin[0] / (this.props.responsive.height - this.graphPaddingTop) - 1;
const y =
2 *
(1 - pin[1] / (this.props.responsive.height - this.graphPaddingTop)) -
1;
const pout = [x + inverse[12], y + inverse[13]];
return [(pout[0] + 1) / 2, (pout[1] + 1) / 2];
};
// transform screen coordinates -> cell coordinates
const invert = pin => {
const x =
(2 * pin[0]) / (this.props.responsive.height - this.graphPaddingTop) -
1;
const y =
2 *
(1 -
pin[1] / (this.props.responsive.height - this.graphPaddingTop)) -
1;
const pout = [
x * inverse[14] + inverse[12],
y * inverse[14] + inverse[13]
];
return [(pout[0] + 1) / 2, (pout[1] + 1) / 2];
};
const brushCoords = {
northwest: invert([s[0][0], s[0][1]]),
southeast: invert([s[1][0], s[1][1]])
};
const brushCoords = {
northwest: invert([s[0][0], s[0][1]]),
southeast: invert([s[1][0], s[1][1]])
};
this.props.dispatch({
type: "graph brush selection change",
brushCoords
});
this.props.dispatch({
type: "graph brush selection change",
brushCoords
});
}
}
handleBrushDeselectAction() {
if (!d3.event.selection) {
if (d3.event && !d3.event.selection) {
this.props.dispatch({
type: "graph brush deselect"
});
}
if (!d3.event) {
/* this line clears the brush procedurally, ie., zoom button clicked, not a click away from brush on svg */
this.state.svg.select(".graph_brush").call(this.state.brush.move, null);
this.props.dispatch({
type: "graph brush deselect"
});
@@ -302,6 +317,7 @@ class Graph extends React.Component {
</button>
<button
onClick={() => {
this.handleBrushDeselectAction();
this.setState({ mode: "zoom" });
}}
style={{
+16 -14
View File
@@ -23,21 +23,23 @@ export const setupSVGandBrushElements = (
.attr("height", side)
.attr("class", `${styles.graphSVG}`);
svg.append("g").call(
d3
.brush()
.extent([
[0, 0],
[
responsive.height - graphPaddingTop,
responsive.height - graphPaddingTop
]
])
.on("brush", handleBrushSelectAction)
.on("end", handleBrushDeselectAction)
);
const brush = d3
.brush()
.extent([
[0, 0],
[responsive.height - graphPaddingTop, responsive.height - graphPaddingTop]
])
.on("brush", handleBrushSelectAction)
.on("end", handleBrushDeselectAction);
const brushContainer = svg
.append("g")
.attr("class", "graph_brush")
.call(brush);
return {
svg
svg,
brushContainer,
brush
};
};
@@ -51,6 +51,7 @@ class Scatterplot extends React.Component {
constructor(props) {
super(props);
this.count = 0;
this.axes = false;
this.state = {
svg: null,
// ctx: null,
@@ -63,8 +64,27 @@ class Scatterplot extends React.Component {
componentDidMount() {
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({
svg
svg,
xScale: scales ? scales.xScale : null,
yScale: scales ? scales.yScale : null
});
const camera = _camera(this.reglCanvas, { scale: true, rotate: false });
@@ -104,21 +124,36 @@ class Scatterplot extends React.Component {
});
}
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) {
if (
this.state.svg &&
this.state.xScale &&
this.state.yScale &&
this.props.scatterplotXXaccessor &&
this.props.scatterplotYYaccessor &&
(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 (
this.props.metadata &&
this.state.regl &&
this.state.pointBuffer &&
this.state.colorBuffer &&
@@ -179,47 +214,32 @@ class Scatterplot extends React.Component {
this.count = cellCount;
}
}
maybeSetupScalesAndDrawAxes(nextProps) {
if (
nextProps.expression &&
nextProps.expression.data &&
nextProps.scatterplotXXaccessor &&
nextProps.scatterplotYYaccessor
) {
const xScale = d3
.scaleLinear()
.domain(
d3.extent(nextProps.expression.data.cells, (cell, i) => {
return cell.e[
nextProps.expression.data.genes.indexOf(
nextProps.scatterplotXXaccessor
)
];
})
)
.range([0, width]);
setupScales(expression, scatterplotXXaccessor, scatterplotYYaccessor) {
const xScale = d3
.scaleLinear()
.domain(
d3.extent(expression.data.cells, (cell, i) => {
return cell.e[expression.data.genes.indexOf(scatterplotXXaccessor)];
})
)
.range([0, width]);
const yScale = d3
.scaleLinear()
.domain(
d3.extent(nextProps.expression.data.cells, cell => {
return cell.e[
nextProps.expression.data.genes.indexOf(
nextProps.scatterplotYYaccessor
)
];
})
)
.range([height, 0]);
const yScale = d3
.scaleLinear()
.domain(
d3.extent(expression.data.cells, cell => {
return cell.e[expression.data.genes.indexOf(scatterplotYYaccessor)];
})
)
.range([height, 0]);
this.setState({
xScale,
yScale
});
}
return {
xScale,
yScale
};
}
drawAxesSVG(xScale, yScale) {
this.state.svg.selectAll("*").remove();
drawAxesSVG(xScale, yScale, svg) {
svg.selectAll("*").remove();
// 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);
@@ -227,28 +247,28 @@ class Scatterplot extends React.Component {
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.
this.state.svg
svg
.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(xAxis);
// y-axis is translated to (0,0)
this.state.svg
svg
.append("g")
.attr("transform", "translate(0,0)")
.attr("class", "y axis")
.call(yAxis);
// adding label. For x-axis, it's at (10, 10), and for y-axis at (width, height-10).
this.state.svg
svg
.append("text")
.attr("x", 10)
.attr("y", 10)
.attr("class", "label")
.text(this.props.scatterplotYYaccessor);
this.state.svg
svg
.append("text")
.attr("x", width)
.attr("y", height - 10)