color by geneset stub

This commit is contained in:
Colin Megill
2020-08-25 15:21:42 -04:00
parent cc6416168b
commit 9f03491094
12 changed files with 291 additions and 204 deletions
+11 -23
View File
@@ -2,7 +2,6 @@
import React from "react";
import { Button } from "@blueprintjs/core";
import { connect } from "react-redux";
import emoji from "react-easy-emoji";
import * as globals from "../../globals";
import Category from "./category";
import { AnnotationsHelpers, ControlsHelpers } from "../../util/stateManager";
@@ -141,6 +140,17 @@ class Categories extends React.Component {
padding: globals.leftSidebarSectionPadding,
}}
>
{writableCategoriesEnabled ? (
<div>
<Button
data-testid="open-annotation-dialog"
onClick={this.handleEnableAnnoMode}
intent="primary"
>
Create new <strong>category</strong>
</Button>
</div>
) : null}
<AnnoDialog
isActive={createAnnoModeActive}
title="Create new category"
@@ -202,28 +212,6 @@ class Categories extends React.Component {
/>
) : null
)}
{writableCategoriesEnabled ? (
<div>
<Button
data-testid="open-annotation-dialog"
onClick={this.handleEnableAnnoMode}
intent="none"
>
<span
style={{
marginRight: 4,
position: "relative",
top: 1,
left: -1,
}}
>
{emoji("🧫")}
</span>
Create new <strong>category</strong>
</Button>
</div>
) : null}
</div>
);
}
@@ -670,8 +670,8 @@ class CategoryValue extends React.Component {
display={isColorBy && categoryValueIndices ? "auto" : "none"}
style={{
marginLeft: 5,
width: VALUE_HEIGHT,
height: VALUE_HEIGHT,
width: 15,
height: 15,
backgroundColor:
isColorBy && categoryValueIndices
? colorScale(categoryValueIndices.get(label))
+2 -3
View File
@@ -55,15 +55,14 @@ class Embedding extends React.PureComponent {
<Button
type="button"
data-testid="layout-choice"
icon="heatmap"
icon="heat-grid"
// minimal
id="embedding"
style={{
cursor: "pointer",
}}
>
{layoutChoice?.current}: {crossfilter.countSelected()} out of{" "}
{crossfilter.size()} cells
heatmap
</Button>
</Tooltip>
}
@@ -1,117 +1,83 @@
// import React from "react";
// import { connect } from "react-redux";
// import AnnoDialog from "../annoDialog";
// // import LabelInput from "../labelInput";
// // import { labelPrompt, isLabelErroneous } from "../labelUtil";
// import actions from "../../actions";
import React from "react";
import { connect } from "react-redux";
import AnnoDialog from "../categorical/annoDialog";
// import LabelInput from "../labelInput";
// import { genesetPrompt, isGenesetErroneous } from "../genesetUtil";
// @connect((state) => ({
// annotations: state.annotations,
// schema: state.annoMatrix?.schema,
// ontology: state.ontology,
// obsCrossfilter: state.obsCrossfilter,
// }))
// class CreateGenesetDialogue extends React.PureComponent {
// constructor(props) {
// super(props);
// this.state = {
// newLabelText: "",
// };
// }
@connect((state) => ({
annotations: state.annotations,
schema: state.annoMatrix?.schema,
ontology: state.ontology,
obsCrossfilter: state.obsCrossfilter,
genesets: state.genesets,
}))
class CreateGenesetDialogue extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
genesetName: "",
};
}
// disableAddNewLabelMode = (e) => {
// const { dispatch } = this.props;
// this.setState({
// newLabelText: "",
// });
// dispatch({
// type: "annotation: disable add new label mode",
// });
// if (e) e.preventDefault();
// };
disableCreateGenesetMode = (e) => {
const { dispatch } = this.props;
this.setState({
genesetName: "",
});
dispatch({
type: "geneset: disable create geneset mode",
});
if (e) e.preventDefault();
};
// handleAddNewLabelToCategory = (e) => {
// const { dispatch, metadataField } = this.props;
// const { newLabelText } = this.state;
createGeneset = (e) => {
const { dispatch } = this.props;
const { genesetName } = this.state;
// this.disableAddNewLabelMode();
// dispatch(
// actions.annotationCreateLabelInCategory(
// metadataField,
// newLabelText,
// false
// )
// );
// e.preventDefault();
// };
dispatch({ type: "geneset: create", genesetName });
e.preventDefault();
};
// addLabelAndAssignCells = (e) => {
// const { dispatch, metadataField } = this.props;
// const { newLabelText } = this.state;
genesetNameError = () => {
/* todo genesets validation */
return false;
};
// this.disableAddNewLabelMode();
// dispatch(
// actions.annotationCreateLabelInCategory(metadataField, newLabelText, true)
// );
// e.preventDefault();
// };
instruction = () => {
return "New, unique geneset";
/* todo genesets */
// return genesetPrompt(this.genesetNameError(geneset), "New, unique geneset", ":");
};
// labelNameError = (name) => {
// const { metadataField, ontology, schema } = this.props;
// return isLabelErroneous(name, metadataField, ontology, schema);
// };
render() {
const { genesetName } = this.state;
const { metadataField, genesets } = this.props;
console.log("dialogue", this.props);
// instruction = (label) => {
// return labelPrompt(this.labelNameError(label), "New, unique label", ":");
// };
return (
<>
<AnnoDialog
isActive={genesets.createGenesetModeActive}
inputProps={{ "data-testid": `${metadataField}:create-label-dialog` }}
primaryButtonProps={{
"data-testid": `${metadataField}:submit-label`,
}}
title="Create new gene set"
instruction="Give your geneset a name" /* todo genesets this.instruction(genesetName) */
cancelTooltipContent="Close this dialog without creating a gene set."
primaryButtonText="Create geneset"
handleSecondaryButtonSubmit={this.addLabelAndAssignCells}
text={genesetName}
validationError={() => {
/* todo genesets this.labelNameError(genesetName) */
return false;
}}
handleSubmit={this.handleAddNewLabelToCategory}
handleCancel={this.disableCreateGenesetMode}
/>
</>
);
}
}
// handleChangeOrSelect = (label) => {
// this.setState({ newLabelText: label });
// };
// render() {
// const { newLabelText } = this.state;
// const { metadataField, annotations, ontology, obsCrossfilter } = this.props;
// const ontologyEnabled = ontology?.enabled ?? false;
// return (
// <>
// <AnnoDialog
// isActive={
// annotations.isAddingNewLabel &&
// annotations.categoryAddingNewLabel === metadataField
// }
// inputProps={{ "data-testid": `${metadataField}:create-label-dialog` }}
// primaryButtonProps={{
// "data-testid": `${metadataField}:submit-label`,
// }}
// title="Add new label to category"
// instruction={this.instruction(newLabelText)}
// cancelTooltipContent="Close this dialog without adding a label."
// primaryButtonText="Add label"
// secondaryButtonText={`Add label & assign ${obsCrossfilter.countSelected()} selected cells`}
// handleSecondaryButtonSubmit={this.addLabelAndAssignCells}
// text={newLabelText}
// validationError={this.labelNameError(newLabelText)}
// handleSubmit={this.handleAddNewLabelToCategory}
// handleCancel={this.disableAddNewLabelMode}
// annoInput={
// <LabelInput
// labelSuggestions={ontologyEnabled ? ontology.terms : null}
// onChange={this.handleChangeOrSelect}
// onSelect={this.handleChangeOrSelect}
// inputProps={{
// "data-testid": `${metadataField}:new-label-name`,
// leftIcon: "tag",
// intent: "none",
// autoFocus: true,
// }}
// />
// }
// />
// </>
// );
// }
// }
// export default CreateGenesetDialogue;
export default CreateGenesetDialogue;
+83 -8
View File
@@ -2,26 +2,101 @@
/* rc slider https://www.npmjs.com/package/rc-slider */
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import HistogramBrush from "../brushableHistogram";
import { Button, ButtonGroup, Tooltip } from "@blueprintjs/core";
import actions from "../../actions";
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
@connect((state) => {
import { AnchorButton } from "@blueprintjs/core";
import Truncate from "../util/truncate";
import TestMiniHisto from "./test_miniHisto";
import * as globals from "../../globals";
@connect(() => {
return {};
})
class Gene extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.state = {
geneIsExpanded: false,
haveFetched: false,
};
}
onColorChangeClick = () => {
const { dispatch, gene } = this.props;
dispatch({
type: "color by expression",
colorAccessor: gene,
});
};
handleGeneExpandClick = () => {
const { geneIsExpanded, haveFetched } = this.state;
this.setState({ geneIsExpanded: !geneIsExpanded });
if (!haveFetched) {
// this.fetchGene(); TODO
}
};
render() {
const { gene } = this.props;
const { geneIsExpanded } = this.state;
return (
<div>
<p> {gene} </p>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "baseline",
marginLeft: 15,
}}
>
<span
role="menuitem"
tabIndex="0"
data-testclass="gene-expand"
data-testid={`${gene}:gene-expand`}
onKeyPress={/* todo_genesets */ () => {}}
style={{
cursor: "pointer",
}}
onClick={this.handleGeneExpandClick}
>
{geneIsExpanded ? (
<FaChevronDown
data-testclass="gene-expand-is-expanded"
style={{ fontSize: 10, marginRight: 5 }}
/>
) : (
<FaChevronRight
data-testclass="gene-expand-is-not-expanded"
style={{ fontSize: 10, marginRight: 5 }}
/>
)}
<Truncate>
<span
style={{
width:
globals.leftSidebarWidth -
310 /* todo_genesets this magic number determines how much of a long geneset name we see, and will be tweaked as we build */,
}}
data-testid={`${gene}:gene-label`}
>
{gene}
</span>
</Truncate>
<TestMiniHisto />
</span>
<AnchorButton
minimal
data-testclass="colorby"
data-testid={`colorby-${gene}`}
onClick={/* todo gene sets */ () => {}}
active={false /* todo gene sets */}
intent="none"
icon="tint"
/>
</div>
);
}
+68 -34
View File
@@ -4,18 +4,21 @@
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import { Icon } from "@blueprintjs/core";
import * as globals from "../../globals";
import { AnchorButton } from "@blueprintjs/core";
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
import actions from "../../actions";
import Gene from "./gene";
import { memoize } from "../../util/dataframe/util";
import Truncate from "../util/truncate";
import TestMiniHisto from "./test_miniHisto";
import * as globals from "../../globals";
@connect((state) => {
@connect((state, ownProps) => {
return {
world: state.world,
userDefinedGenes: state.controls.userDefinedGenes,
userDefinedGenesLoading: state.controls.userDefinedGenesLoading,
isColorAccessor: state.colors.colorAccessor === ownProps.setName,
};
})
class GeneSet extends React.Component {
@@ -89,43 +92,74 @@ class GeneSet extends React.Component {
}
};
onColorChangeClick = () => {
const { dispatch, setName } = this.props;
dispatch({
type: "color by gene set",
colorAccessor: setName,
});
};
render() {
const { setName, setGenes } = this.props;
const { setName, setGenes, isColorAccessor } = this.props;
const { isOpen } = this.state;
// console.log("geneSet setGenes variable: ", setGenes);
return (
<div>
<span
role="menuitem"
tabIndex="0"
data-testclass="geneset-expand"
data-testid={`${setName}:geneset-expand`}
onKeyPress={/* todo */ () => {}}
<div style={{ marginBottom: 3 }}>
<div
style={{
cursor: "pointer",
display: "flex",
justifyContent: "space-between",
alignItems: "baseline",
}}
onClick={this.onGenesetMenuClick}
>
<Truncate>
<span
style={{
maxWidth: globals.leftSidebarWidth - 150,
}}
data-testid={`${setName}:geneset-label`}
>
{setName}
</span>
</Truncate>
<Icon
data-testclass={
isOpen
? "geneset-expand-is-expanded"
: "geneset-expand-is-not-expanded"
}
icon={isOpen ? "caret-down" : "caret-right"}
iconSize={14}
/>
</span>
<span
role="menuitem"
tabIndex="0"
data-testclass="geneset-expand"
data-testid={`${setName}:geneset-expand`}
onKeyPress={/* todo_genesets */ () => {}}
style={{
cursor: "pointer",
}}
onClick={this.onGenesetMenuClick}
>
{isOpen ? (
<FaChevronDown
data-testclass="geneset-expand-is-expanded"
style={{ fontSize: 10, marginRight: 5 }}
/>
) : (
<FaChevronRight
data-testclass="geneset-expand-is-not-expanded"
style={{ fontSize: 10, marginRight: 5 }}
/>
)}
<Truncate>
<span
style={{
maxWidth:
globals.leftSidebarWidth -
200 /* todo_genesets this magic number determines how much of a long geneset name we see, and will be tweaked as we build */,
}}
data-testid={`${setName}:geneset-label`}
>
{setName}
</span>
</Truncate>
</span>
<div>
<TestMiniHisto />
<AnchorButton
data-testclass="colorby"
data-testid={`colorby-${setName}`}
onClick={this.onColorChangeClick}
active={isColorAccessor}
intent={isColorAccessor ? "primary" : "none"}
icon="tint"
/>
</div>
</div>
{isOpen
? _.map(setGenes, (gene) => {
+6 -21
View File
@@ -3,13 +3,12 @@
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import emoji from "react-easy-emoji";
import { Button } from "@blueprintjs/core";
import HistogramBrush from "../brushableHistogram";
import * as globals from "../../globals";
import GeneSet from "./geneSet";
import testGeneSets from "./test_data";
import CreateGenesetDialogue from "./createGenesetDialogue";
@connect((state) => {
return {
@@ -39,35 +38,21 @@ class GeneExpression extends React.Component {
render() {
const { userDefinedGenes, differential } = this.props;
const geneSetsFeatureEnabledTODO = true;
console.log("genesets", this.props);
return (
<div
style={{
borderBottom: `1px solid ${globals.lighterGrey}`,
}}
>
<div>
<div>
{geneSetsFeatureEnabledTODO ? (
<div>
<div style={{ marginBottom: 10, position: "relative", top: -2 }}>
<Button
data-testid="open-create-geneset-dialog"
onClick={this.handleActivateCreateGenesetMode}
intent="none"
intent="primary"
>
<span
style={{
marginRight: 4,
position: "relative",
top: 1,
left: -1,
}}
>
{emoji("🧬")}
</span>
Create new <strong>geneset</strong>
Create new <strong>gene set</strong>
</Button>
</div>
) : null}
<CreateGenesetDialogue />
{/* <AddGenes /> */}
{userDefinedGenes.length > 0
? _.map(userDefinedGenes, (geneName, index) => {
@@ -0,0 +1,23 @@
import React from "react";
export default () => (
<svg width="100" height="11" viewBox="0 0 100 11" fill="none">
<rect width="100" height="11" fill="url(#pattern0)" />
<defs>
<pattern
id="pattern0"
patternContentUnits="objectBoundingBox"
width="1"
height="1"
>
<use xlinkHref="#image0" transform="scale(0.01 0.0909091)" />
</pattern>
<image
id="image0"
width="100"
height="11"
xlinkHref="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAALCAYAAACJQCjbAAABP0lEQVRYCe2XPUqEMRRFD+iIIPYWFi7Awi0olgqW4s8C7BVsLdyBW7B0A+IGXIHTTTPrULnwLoSQSL5xBsQvDw7XvNyEzH0MgzC8XgDxFcwBcRIMv7GfWCiBDUB4EDU9BkSvFSdQG0Cp/wlsByt+1nivLwVf62kg3jsFRK8lJfAACAfcoulA7H8HUvy8CSB6NSbgQIdoaSD5+Q9AqC//TtD4rPHa8iBb1i0DSe+xX/oUXACiVyTwDIg0uNa/HfBQf35O62lwBojR1T0gWsMs+fJgS560Z7/Ve/la/cfgErgCDoJ/M6hrQMwCh/EbLQX50332W+3N13nf+1K//w0QN8EtcAccAkfAfvBnB+gPuUx1UK132m/1uXyd971f05pf/ddA3zLh36xzQGh4Yi/YBcRWsA6ItWATEP4HeqGBfwNJGzl7D23hZwAAAABJRU5ErkJggg=="
/>
</defs>
</svg>
);
@@ -5,6 +5,7 @@ import Categorical from "../categorical";
import * as globals from "../../globals";
import DynamicScatterplot from "../scatterplot/scatterplot";
import TopLeftLogoAndTitle from "./topLeftLogoAndTitle";
import Continuous from "../continuous/continuous";
@connect((state) => ({
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
@@ -32,6 +33,7 @@ class LeftSideBar extends React.Component {
}}
>
<Categorical />
<Continuous />
</div>
{scatterplotXXaccessor && scatterplotYYaccessor ? (
<DynamicScatterplot />
@@ -1,7 +1,6 @@
// jshint esversion: 6
import React from "react";
import { connect } from "react-redux";
import Continuous from "../continuous/continuous";
import GeneExpression from "../geneExpression";
import * as globals from "../../globals";
@@ -26,7 +25,6 @@ class RightSidebar extends React.Component {
}}
>
<GeneExpression />
<Continuous />
</div>
);
}
+1
View File
@@ -66,6 +66,7 @@ const ColorsReducer = (
};
}
case "color by gene set":
case "color by categorical metadata":
case "color by continuous metadata": {
/* toggle between this mode and reset */
+19 -3
View File
@@ -3,9 +3,10 @@ Reducers for geneset UI-state.
*/
const GeneSets = (
state = {
addGenesetModeActive: false,
createGenesetModeActive: false,
isEditingGenesetName: false,
genesetAddingNewGenes: null,
userCreatedGenesets: {},
},
action
) => {
@@ -13,10 +14,25 @@ const GeneSets = (
case "geneset: activate add new geneset mode": {
return {
...state,
addGenesetModeActive: true,
createGenesetModeActive: true,
};
}
case "geneset: disable create geneset mode": {
return {
...state,
createGenesetModeActive: false,
};
}
case "geneset: create": {
return {
...state,
userCreatedGenesets: Object.assign(
{},
{ [action.genesetName]: {} },
...state.userCreatedGenesets
),
};
}
default:
return state;
}