+
-
- {userDefinedGenes.length > 0
- ? _.map(userDefinedGenes, (geneName, index) => {
- return (
-
- );
- })
- : null}
-
-
- {differential.diffExp
- ? _.map(differential.diffExp, (value, index) => {
- return (
-
- );
- })
- : null}
+
+
+ Create new gene set
+
+
+
+
+
{this.renderDiffexpGeneSets()}
+
{this.renderGeneSets()}
);
}
diff --git a/client/src/components/geneExpression/menus/addGeneToGenesetDialogue.js b/client/src/components/geneExpression/menus/addGeneToGenesetDialogue.js
new file mode 100644
index 00000000..f8148e19
--- /dev/null
+++ b/client/src/components/geneExpression/menus/addGeneToGenesetDialogue.js
@@ -0,0 +1,92 @@
+import React from "react";
+import { connect } from "react-redux";
+import AnnoDialog from "../../annoDialog";
+import LabelInput from "../../labelInput";
+import parseBulkGeneString from "../../../util/parseBulkGeneString";
+
+@connect((state) => ({
+ genesetsUI: state.genesetsUI,
+}))
+class AddGeneToGenesetDialogue extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ genesToAdd: "",
+ };
+ }
+
+ disableAddGeneMode = () => {
+ const { dispatch } = this.props;
+ dispatch({
+ type: "geneset: disable add new genes mode",
+ });
+ };
+
+ handleAddGeneToGeneSet = (e) => {
+ const { geneset, dispatch } = this.props;
+ const { genesToAdd } = this.state;
+
+ const genesTmpHardcodedFormat = [];
+
+ const genesArrayFromString = parseBulkGeneString(genesToAdd);
+
+ genesArrayFromString.forEach((_gene) => {
+ genesTmpHardcodedFormat.push({
+ geneSymbol: _gene,
+ });
+ });
+
+ dispatch({
+ type: "geneset: add genes",
+ genesetName: geneset,
+ genes: genesTmpHardcodedFormat,
+ });
+ dispatch({
+ type: "geneset: disable add new genes mode",
+ });
+ if (e) e.preventDefault();
+ };
+
+ handleChange = (e) => {
+ this.setState({ genesToAdd: e });
+ };
+
+ render() {
+ const { geneset, genesetsUI } = this.props;
+ const { genesToAdd } = this.state;
+
+ return (
+ <>
+
+ }
+ handleSubmit={this.handleAddGeneToGeneSet}
+ handleCancel={this.disableAddGeneMode}
+ />
+ >
+ );
+ }
+}
+
+export default AddGeneToGenesetDialogue;
diff --git a/client/src/components/geneExpression/addGenes.js b/client/src/components/geneExpression/menus/addGenes.js
similarity index 96%
rename from client/src/components/geneExpression/addGenes.js
rename to client/src/components/geneExpression/menus/addGenes.js
index 2d1ca1b4..f9bdda99 100644
--- a/client/src/components/geneExpression/addGenes.js
+++ b/client/src/components/geneExpression/menus/addGenes.js
@@ -1,7 +1,4 @@
-/* rc slider https://www.npmjs.com/package/rc-slider */
-
import React from "react";
-import _ from "lodash";
import fuzzysort from "fuzzysort";
import { connect } from "react-redux";
import { Suggest } from "@blueprintjs/select";
@@ -13,14 +10,15 @@ import {
Intent,
MenuItem,
} from "@blueprintjs/core";
-import * as globals from "../../globals";
-import actions from "../../actions";
+import * as globals from "../../../globals";
+import actions from "../../../actions";
import {
postUserErrorToast,
keepAroundErrorToast,
-} from "../framework/toasters";
+} from "../../framework/toasters";
-import { memoize } from "../../util/dataframe/util";
+import { memoize } from "../../../util/dataframe/util";
+import parseBulkGeneString from "../../../util/parseBulkGeneString";
const renderGene = (fuzzySortResult, { handleClick, modifiers }) => {
if (!modifiers.matchesPredicate) {
@@ -101,7 +99,7 @@ class AddGenes extends React.Component {
Apod,,, Cd74,, ,,, Foo, Bar-2,,
*/
if (bulkAdd !== "") {
- const genes = _.pull(_.uniq(bulkAdd.split(/[ ,]+/)), "");
+ const genes = parseBulkGeneString(bulkAdd);
if (genes.length === 0) {
return keepAroundErrorToast("Must enter a gene name.");
}
diff --git a/client/src/components/geneExpression/menus/createGenesetDialogue.js b/client/src/components/geneExpression/menus/createGenesetDialogue.js
new file mode 100644
index 00000000..7fc4bd55
--- /dev/null
+++ b/client/src/components/geneExpression/menus/createGenesetDialogue.js
@@ -0,0 +1,149 @@
+import _ from "lodash";
+import React from "react";
+import { connect } from "react-redux";
+import AnnoDialog from "../../annoDialog";
+import LabelInput from "../../labelInput";
+
+@connect((state) => ({
+ annotations: state.annotations,
+ schema: state.annoMatrix?.schema,
+ ontology: state.ontology,
+ obsCrossfilter: state.obsCrossfilter,
+ genesets: state.genesets.genesets,
+ genesetsUI: state.genesetsUI,
+}))
+class CreateGenesetDialogue extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ genesetName: "",
+ genesToPopulateGeneset: "",
+ };
+ }
+
+ disableCreateGenesetMode = (e) => {
+ const { dispatch } = this.props;
+ this.setState({
+ genesetName: "",
+ genesToPopulateGeneset: "",
+ });
+ dispatch({
+ type: "geneset: disable create geneset mode",
+ });
+ if (e) e.preventDefault();
+ };
+
+ createGeneset = (e) => {
+ const { dispatch } = this.props;
+ const { genesetName, genesToPopulateGeneset } = this.state;
+
+ dispatch({
+ type: "geneset: create",
+ genesetName,
+ genesetDescription: "",
+ });
+ if (genesToPopulateGeneset) {
+ const genesTmpHardcodedFormat = [];
+
+ const genesArrayFromString = _.pull(
+ _.uniq(genesToPopulateGeneset.split(/[ ,]+/)),
+ ""
+ );
+
+ genesArrayFromString.forEach((_gene) => {
+ genesTmpHardcodedFormat.push({
+ geneSymbol: _gene,
+ });
+ });
+
+ dispatch({
+ type: "geneset: add genes",
+ genesetName,
+ genes: genesTmpHardcodedFormat,
+ });
+ }
+ dispatch({
+ type: "geneset: disable create geneset mode",
+ });
+ this.setState({
+ genesetName: "",
+ genesToPopulateGeneset: "",
+ });
+ e.preventDefault();
+ };
+
+ genesetNameError = () => {
+ return false;
+ };
+
+ handleChange = (e) => {
+ this.setState({ genesetName: e });
+ };
+
+ handleGenesetInputChange = (e) => {
+ this.setState({ genesToPopulateGeneset: e });
+ };
+
+ instruction = (genesetName, genesets) => {
+ return genesets.has(genesetName)
+ ? "Geneset name must be unique."
+ : "New, unique geneset name";
+ };
+
+ validate = (genesetName, genesets) => {
+ return genesets.has(genesetName);
+ };
+
+ render() {
+ const { genesetName } = this.state;
+ const { metadataField, genesetsUI, genesets } = this.props;
+
+ return (
+ <>
+
+ }
+ secondaryInstructions="Optionally add a list of comma separated genes to populate the gene set"
+ secondaryInput={
+
+ }
+ handleSubmit={this.createGeneset}
+ handleCancel={this.disableCreateGenesetMode}
+ />
+ >
+ );
+ }
+}
+
+export default CreateGenesetDialogue;
diff --git a/client/src/components/geneExpression/menus/editGenesetNameDialogue.js b/client/src/components/geneExpression/menus/editGenesetNameDialogue.js
new file mode 100644
index 00000000..0ac6e724
--- /dev/null
+++ b/client/src/components/geneExpression/menus/editGenesetNameDialogue.js
@@ -0,0 +1,95 @@
+import React from "react";
+import { connect } from "react-redux";
+import AnnoDialog from "../../annoDialog";
+import LabelInput from "../../labelInput";
+
+@connect((state) => ({
+ annotations: state.annotations,
+ schema: state.annoMatrix?.schema,
+ ontology: state.ontology,
+ obsCrossfilter: state.obsCrossfilter,
+ genesetsUI: state.genesetsUI,
+}))
+class RenameGeneset extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ genesetName: "",
+ };
+ }
+
+ disableEditGenesetNameMode = (e) => {
+ const { dispatch } = this.props;
+ this.setState({
+ genesetName: "",
+ });
+ dispatch({
+ type: "geneset: disable rename geneset mode",
+ });
+ if (e) e.preventDefault();
+ };
+
+ renameGeneset = (e) => {
+ const { dispatch, genesetsUI } = this.props;
+ const { genesetName } = this.state;
+
+ dispatch({
+ type: "geneset: update",
+ genesetName: genesetsUI.isEditingGenesetName,
+ update: { genesetName },
+ });
+ dispatch({
+ type: "geneset: disable rename geneset mode",
+ });
+ e.preventDefault();
+ };
+
+ genesetNameError = () => {
+ return false;
+ };
+
+ handleChange = (e) => {
+ this.setState({ genesetName: e });
+ };
+
+ render() {
+ const { genesetName } = this.state;
+ const { genesetsUI } = this.props;
+
+ return (
+ <>
+
+ }
+ handleSubmit={this.renameGeneset}
+ handleCancel={this.disableEditGenesetNameMode}
+ />
+ >
+ );
+ }
+}
+
+export default RenameGeneset;
diff --git a/client/src/components/geneExpression/menus/genesetMenus.js b/client/src/components/geneExpression/menus/genesetMenus.js
new file mode 100644
index 00000000..ccdfe9c5
--- /dev/null
+++ b/client/src/components/geneExpression/menus/genesetMenus.js
@@ -0,0 +1,120 @@
+import React from "react";
+import { connect } from "react-redux";
+import {
+ Button,
+ Menu,
+ MenuItem,
+ Popover,
+ Position,
+ Tooltip,
+ Icon,
+ PopoverInteractionKind,
+} from "@blueprintjs/core";
+
+import * as globals from "../../../globals";
+import AddGeneToGenesetDialogue from "./addGeneToGenesetDialogue";
+
+@connect((state) => {
+ return {
+ genesetsUI: state.genesetsUI,
+ };
+})
+class GenesetMenus extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+
+ activateAddGeneToGenesetMode = () => {
+ const { dispatch, geneset } = this.props;
+ dispatch({
+ type: "geneset: activate add new genes mode",
+ geneset,
+ });
+ };
+
+ activateEditGenesetNameMode = () => {
+ const { dispatch, geneset } = this.props;
+
+ dispatch({
+ type: "geneset: activate rename geneset mode",
+ data: geneset,
+ });
+ };
+
+ handleDeleteCategory = () => {
+ const { dispatch, geneset } = this.props;
+ dispatch({ type: "geneset: delete", genesetName: geneset });
+ };
+
+ render() {
+ const { geneset, genesetsEditable, createText } = this.props;
+
+ return (
+ <>
+ {genesetsEditable ? (
+ <>
+
+ }
+ onClick={this.activateAddGeneToGenesetMode}
+ small
+ minimal
+ />
+
+
+
+
+
+
+ }
+ >
+ }
+ small
+ minimal
+ />
+
+
}
+ />
+ >
+ ) : null}
+ >
+ );
+ }
+}
+
+export default GenesetMenus;
diff --git a/client/src/components/graph/graph.js b/client/src/components/graph/graph.js
index ceaf31c7..51c98ef9 100644
--- a/client/src/components/graph/graph.js
+++ b/client/src/components/graph/graph.js
@@ -1,4 +1,3 @@
-// jshint esversion: 6
import React from "react";
import * as d3 from "d3";
import { connect, shallowEqual } from "react-redux";
diff --git a/client/src/components/graph/setupLasso.js b/client/src/components/graph/setupLasso.js
index 6610e04c..095b4ec6 100644
--- a/client/src/components/graph/setupLasso.js
+++ b/client/src/components/graph/setupLasso.js
@@ -1,5 +1,3 @@
-// https://bl.ocks.org/pbeshai/8008075f9ce771ee8be39e8c38907570
-
import * as d3 from "d3";
import { Colors } from "@blueprintjs/core";
diff --git a/client/src/components/graph/setupSVGandBrush.js b/client/src/components/graph/setupSVGandBrush.js
index d6a77bc4..bedf96a9 100644
--- a/client/src/components/graph/setupSVGandBrush.js
+++ b/client/src/components/graph/setupSVGandBrush.js
@@ -1,4 +1,3 @@
-// jshint esversion: 6
import * as d3 from "d3";
import Lasso from "./setupLasso";
diff --git a/client/src/components/categorical/labelInput.js b/client/src/components/labelInput.js
similarity index 100%
rename from client/src/components/categorical/labelInput.js
rename to client/src/components/labelInput.js
diff --git a/client/src/components/leftSidebar/index.js b/client/src/components/leftSidebar/index.js
index 4cc60658..6d72b424 100644
--- a/client/src/components/leftSidebar/index.js
+++ b/client/src/components/leftSidebar/index.js
@@ -1,10 +1,10 @@
-// jshint esversion: 6
import React from "react";
import { connect } from "react-redux";
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 +32,7 @@ class LeftSideBar extends React.Component {
}}
>
+
{scatterplotXXaccessor && scatterplotYYaccessor ? (