Remove heatmap, add diffexp info to histograms (#339)

* Create Release_Validation_Recipe.md

* compose middle ware for redux devtools

* update package.json and lock

* remove heatmap, add to left side bar

* redux devtools extension
This commit is contained in:
Colin Megill
2018-10-16 16:29:20 -07:00
committed by GitHub
parent 76ec29734a
commit 952578680a
11 changed files with 266 additions and 100 deletions
+34 -29
View File
@@ -2054,7 +2054,7 @@
},
"babel-plugin-syntax-object-rest-spread": {
"version": "6.13.0",
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=",
"dev": true
},
@@ -2489,7 +2489,7 @@
},
"browserify-aes": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
@@ -2526,7 +2526,7 @@
},
"browserify-rsa": {
"version": "4.0.1",
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"dev": true,
"requires": {
@@ -2580,7 +2580,7 @@
},
"buffer": {
"version": "4.9.1",
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": {
@@ -3258,7 +3258,7 @@
},
"create-hash": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
@@ -3271,7 +3271,7 @@
},
"create-hmac": {
"version": "1.1.7",
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
@@ -3945,7 +3945,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"requires": {
@@ -4522,7 +4522,7 @@
},
"load-json-file": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
@@ -4699,7 +4699,7 @@
},
"events": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
},
"evp_bytestokey": {
@@ -4993,7 +4993,7 @@
},
"finalhandler": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"dev": true,
"requires": {
@@ -5717,7 +5717,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
},
@@ -6096,7 +6096,7 @@
},
"html-webpack-plugin": {
"version": "3.2.0",
"resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
"integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
"dev": true,
"requires": {
@@ -6172,7 +6172,7 @@
},
"http-errors": {
"version": "1.6.3",
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"dev": true,
"requires": {
@@ -6395,7 +6395,7 @@
},
"is-builtin-module": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
"dev": true,
"requires": {
@@ -7394,7 +7394,7 @@
},
"json5": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
},
@@ -7484,7 +7484,7 @@
},
"load-json-file": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
"dev": true,
"requires": {
@@ -7813,7 +7813,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -7938,7 +7938,7 @@
},
"minimist": {
"version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
@@ -7983,7 +7983,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
@@ -9695,7 +9695,7 @@
},
"parse-asn1": {
"version": "5.1.1",
"resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
"integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==",
"dev": true,
"requires": {
@@ -10291,7 +10291,7 @@
},
"react-redux": {
"version": "5.0.7",
"resolved": "http://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.7.tgz",
"integrity": "sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg==",
"requires": {
"hoist-non-react-statics": "^2.5.0",
@@ -10355,7 +10355,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -10698,6 +10698,11 @@
"symbol-observable": "^1.2.0"
}
},
"redux-devtools-extension": {
"version": "2.13.5",
"resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.5.tgz",
"integrity": "sha512-QQ9BRy77oURHMdGys9rfQcCQDzXZ1T4oW+eUyE5Cg7DNVau69HJzc4YNDMOmpi0Dzpi1zOQgQ2rUpgJta4Lfqg=="
},
"redux-thunk": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
@@ -11368,7 +11373,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
@@ -11555,7 +11560,7 @@
},
"sha.js": {
"version": "2.4.11",
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
@@ -12009,7 +12014,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
@@ -12245,7 +12250,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
@@ -12884,7 +12889,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -13542,7 +13547,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true,
"requires": {
@@ -13650,7 +13655,7 @@
},
"yargs": {
"version": "11.1.0",
"resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
"integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
"dev": true,
"requires": {
+1
View File
@@ -47,6 +47,7 @@
"react-icons": "^3.2.0",
"react-redux": "^5.0.6",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.5",
"redux-thunk": "^2.2.0",
"regl": "^1.3.1",
"scroll-speed": "^1.0.0",
@@ -16,7 +16,10 @@ import actions from "../../actions";
@connect(state => ({
world: state.controls.world,
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
crossfilter: state.controls.crossfilter,
differential: state.differential,
initializeRanges: _.get(state.controls.world, "summary.obs"),
colorAccessor: state.controls.colorAccessor,
colorScale: state.controls.colorScale,
@@ -208,8 +211,38 @@ class HistogramBrush extends React.Component {
}
}
handleSetGeneAsScatterplotX() {
return () => {
const { dispatch, field } = this.props;
dispatch({
type: "set scatterplot x",
data: field
});
};
}
handleSetGeneAsScatterplotY() {
return () => {
const { dispatch, field } = this.props;
dispatch({
type: "set scatterplot y",
data: field
});
};
}
render() {
const { field, ranges, colorAccessor, isUserDefined } = this.props;
const {
field,
colorAccessor,
isUserDefined,
isDiffExp,
diffExp_avgDiff,
diffExp_set1AvgExp,
diffExp_set2AvgExp,
scatterplotXXaccessor,
scatterplotYYaccessor
} = this.props;
return (
<div
@@ -234,6 +267,84 @@ class HistogramBrush extends React.Component {
remove
</span>
) : null}
{isDiffExp ? (
<span>
<span
style={{
fontSize: globals.tiniestFontSize,
marginLeft: 7
}}
>
<strong>1:</strong>
{` ${diffExp_set1AvgExp.toPrecision(2)}`}
</span>
<span
style={{
fontSize: globals.tiniestFontSize,
marginLeft: 7,
backgroundColor: globals.lighterGrey,
padding: 2
}}
>
<strong>2:</strong>
{` ${diffExp_set2AvgExp.toPrecision(2)}`}
</span>
<span
style={{
fontSize: globals.tiniestFontSize,
marginLeft: 7
}}
>
{`Av. Diff: ${diffExp_avgDiff.toFixed(2)}`}
</span>
<span
onClick={this.handleSetGeneAsScatterplotX(field).bind(this)}
style={{
fontSize: 16,
color:
scatterplotXXaccessor === field
? "white"
: globals.brightBlue,
cursor: "pointer",
position: "relative",
top: 1,
fontWeight: 700,
marginRight: 4,
borderRadius: 3,
padding: "2px 3px",
backgroundColor:
scatterplotXXaccessor === field
? globals.brightBlue
: "inherit"
}}
>
X
</span>
<span
onClick={this.handleSetGeneAsScatterplotY(field).bind(this)}
style={{
fontSize: 16,
color:
scatterplotYYaccessor === field
? "white"
: globals.brightBlue,
cursor: "pointer",
position: "relative",
top: 1,
fontWeight: 700,
marginRight: 4,
borderRadius: 3,
padding: "2px 3px",
backgroundColor:
scatterplotYYaccessor === field
? globals.brightBlue
: "inherit"
}}
>
Y
</span>
</span>
) : null}
</div>
<svg
width={this.width}
@@ -3,9 +3,7 @@ import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import { FaChevronRight } from "react-icons/fa";
import { FaChevronDown } from "react-icons/fa";
import { FaPaintBrush } from "react-icons/fa";
import { FaChevronRight, FaChevronDown, FaPaintBrush } from "react-icons/fa";
import * as globals from "../../globals";
import Value from "./value";
@@ -126,7 +124,7 @@ class Category extends React.Component {
cursor: "pointer",
display: "inline-block",
position: "relative",
top: -1
top: 2
}}
onClick={() => {
this.setState({ isExpanded: !isExpanded });
@@ -155,7 +153,7 @@ class Category extends React.Component {
// backgroundColor: this.props.colorAccessor === this.props.metadataField ? globals.brightBlue : "inherit",
display: "inline-block",
position: "relative",
top: -1,
top: 2,
cursor: "pointer"
}}
>
@@ -196,6 +194,7 @@ class Categories extends React.Component {
// overflow: "auto",
}}
>
<p> Categorical Metadata </p>
{_.map(ranges, (value, key) => {
const isColorField = key.includes("color") || key.includes("Color");
if (value.options && !isColorField && key !== "name") {
@@ -37,8 +37,6 @@ class Continuous extends React.Component {
};
}
componentDidUpdate() {}
render() {
const { ranges, obsAnnotations, schema } = this.props;
if (schema && !this.continuousChecked) {
@@ -3,7 +3,7 @@ import React from "react";
import _ from "lodash";
import memoize from "memoize-one";
import { connect } from "react-redux";
import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */
// import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */
import getContrast from "font-color-contrast"; // https://www.npmjs.com/package/font-color-contrast
import { FaPaintBrush } from "react-icons/fa";
import * as d3 from "d3";
@@ -93,6 +93,8 @@ class GeneExpression extends React.Component {
style={{
display: "inline-block",
marginLeft: 7,
position: "relative",
top: 5,
color: globals.brightBlue,
fontSize: 22
}}
@@ -135,6 +137,9 @@ class GeneExpression extends React.Component {
field={name}
ranges={d3.extent(values)}
isDiffExp
diffExp_avgDiff={value[1]}
diffExp_set1AvgExp={value[4]}
diffExp_set2AvgExp={value[5]}
/>
);
}
+6 -2
View File
@@ -393,7 +393,9 @@ class Graph extends React.Component {
border:
mode === "brush" ? "1px solid black" : "1px solid white",
backgroundColor: "white",
padding: 5,
padding: "5px 5px 2px 5px",
position: "relative",
top: 2,
marginRight: 10,
borderRadius: 3
}}
@@ -413,7 +415,9 @@ class Graph extends React.Component {
border:
mode === "zoom" ? "1px solid black" : "1px solid white",
backgroundColor: "white",
padding: 5,
padding: "5px 5px 2px 5px",
position: "relative",
top: 2,
marginRight: 10,
borderRadius: 3
}}
+24 -58
View File
@@ -12,7 +12,9 @@ import DynamicScatterplot from "./scatterplot/scatterplot";
@connect(state => ({
responsive: state.responsive,
datasetTitle: _.get(state.config, "displayNames.dataset")
datasetTitle: _.get(state.config, "displayNames.dataset"),
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
}))
class LeftSideBar extends React.Component {
constructor(props) {
@@ -24,86 +26,48 @@ class LeftSideBar extends React.Component {
render() {
const { currentTab } = this.state;
const { responsive, datasetTitle } = this.props;
const {
responsive,
datasetTitle,
scatterplotXXaccessor,
scatterplotYYaccessor
} = this.props;
/*
this magic number should be made less fragile,
if cellxgene logo or tabs change, this must as well
*/
const metadataSectionPadding = currentTab === "metadata" ? 88 : 450;
const metadataSectionPadding =
scatterplotXXaccessor && scatterplotYYaccessor ? 450 : 0;
return (
<div style={{ position: "fixed" }}>
<p
style={{
margin: 10,
position: "fixed",
left: responsive.width / 2,
top: 14,
margin: 0,
fontSize: 24,
color: globals.lightGrey,
color: globals.darkerGrey,
fontWeight: 700,
width: "100%"
}}
>
cellxgene &nbsp;
cellxgene: &nbsp;
{datasetTitle}
</p>
<div style={{ padding: 10 }}>
<button
type="button"
style={{
padding: "none",
outline: 0,
fontSize: 14,
fontWeight: currentTab === "metadata" ? 700 : 400,
fontStyle: currentTab === "metadata" ? "inherit" : "italic",
cursor: "pointer",
border: "none",
backgroundColor: "#FFF",
borderTop: "none",
borderBottom: "none",
borderRight: "none",
borderLeft: "none"
}}
onClick={() => {
this.setState({ currentTab: "metadata" });
}}
>
Metadata
</button>
<button
type="button"
style={{
padding: "none",
outline: 0,
fontSize: 14,
fontWeight: currentTab === "expression" ? 700 : 400,
fontStyle: currentTab === "expression" ? "inherit" : "italic",
cursor: "pointer",
border: "none",
backgroundColor: "#FFF",
borderTop: "none",
borderBottom: "none",
borderRight: "none",
borderLeft: "none"
}}
onClick={() => {
this.setState({ currentTab: "expression" });
}}
>
Expression
</button>
</div>
<div
style={{
height: responsive.height - metadataSectionPadding,
width: 400,
padding: 10,
padding: "0px 10px 10px 10px",
overflowY: "auto",
overflowX: "hidden"
}}
>
{currentTab === "metadata" ? <Categorical /> : null}
{currentTab === "metadata" ? <GeneExpression /> : null}
{currentTab === "metadata" ? <Continuous /> : null}
{currentTab === "expression" ? <Heatmap /> : null}
<Categorical />
<GeneExpression />
<Continuous />
</div>
<div
style={{
@@ -112,7 +76,9 @@ class LeftSideBar extends React.Component {
left: 0
}}
>
{currentTab === "expression" ? <DynamicScatterplot /> : null}
{scatterplotXXaccessor && scatterplotYYaccessor ? (
<DynamicScatterplot />
) : null}
</div>
<div style={{ position: "fixed", bottom: 0, right: 0 }}>
{currentTab === "metadata" ? <ExpressionButtons /> : null}
+4 -2
View File
@@ -3,6 +3,7 @@ import { combineReducers, createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import updateURLMiddleware from "../middleware/updateURLMiddleware";
import updateCellColors from "../middleware/updateCellColors";
import { composeWithDevTools } from "redux-devtools-extension";
import config from "./config";
import differential from "./differential";
@@ -18,8 +19,9 @@ const Reducer = combineReducers({
const store = createStore(
Reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
applyMiddleware(thunk, updateURLMiddleware, updateCellColors)
composeWithDevTools(
applyMiddleware(thunk, updateURLMiddleware, updateCellColors)
)
);
export default store;
+75
View File
@@ -0,0 +1,75 @@
# Pre-release Validation Plan
_This document contains a general purpose test plan for validating cellxgene prior to release_.
All steps are expected to pass with no errors or malfunctions. Tester should check:
* CLI for errors (eg, build error, engine error)
* Browser console for errors
* Expected CLI and/or browser UI function
## [Backend Test](#backend-test)
### Getting started build & install validation:
Goal: validate build, install and example demo correctness, per Getting Started instructions.
1. Clean clone of the cellxgene repo into a local directory
2. Follow build & install instructions from the Getting Started guide
* Confirm no build or install errors
* Confirm `cellxgene --help` functions correctly
3. Follow the example data set demo startup from Getting Started guide and confirm front-end data loads correctly
4. Confirm all package (and other) version dependencies are correct and match
### Tabula Muris
Goal: basic functional validation of b/e functions using Tabula Muris data and the scanpy engine.
1. Precondition: cellxgene built & installed.
2. Load the Tabula Muris data set: `cellxgene --title 'T. Muris' scanpy directory-name/`
3. Verify all metadata selectors display and have correct type/options:
* *TODO:* _need list of metadata and their type_
* ...
4. Verify default graph display has expected layout. _need screen shot of expected layout_
5. Verify selection controls work as expected:
* continuous metadata field
* categorical metadata field
* graph/lasso select
6. Verify color by metadata type
7. Select two cell sets and confirm differential expression compute succeeds
6. Verify expression scatter plot is correct
## [Front-end Compatibility Test](#frontend-compatability-test)
Goal: verify front-end UI compatibility with a given browser variant/version/platform.
1. Start back-end on PBM3K data set
2. Load UI
3. Verify all major UI modes/functions:
* Graph display
* Metadata selector display
* Title display
* Selection - single and multiple fields - correctly display in cluster graph
* Regraph & reset function correctly
* Differential expression calc & scatter plot display
* All graphs maintain consistent selection state
* All selection widgets (eg, continuous metadata selector) maintain correct status (consistent with graph displays)
4. Verify overall performance is reasonable/interactive
5. Verify no errors on CLI or browser console
## [End-to-end Functional Test](#e2e-functional-test)
Goal: confirm end-to-end functional behavior is as expected.
### ScanPy engine
1. Basic data load and display
* ...
2. Select & multi-select of metadata and coordinates
* ...
3. Color by metadata
* ...
4. Regraph / reset
* ...
5. Differential expression: scatterplot, top-N genes, etc.
* ...
6. Color by expression
* ...
7. Arbitrary gene expression
* ...