mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-02 16:18:12 +08:00
@@ -13,6 +13,13 @@ A "user" error - eg, bad input
|
|||||||
export const postUserErrorToast = message =>
|
export const postUserErrorToast = message =>
|
||||||
ErrorToastTopCenter.show({ message, intent: Intent.WARNING });
|
ErrorToastTopCenter.show({ message, intent: Intent.WARNING });
|
||||||
|
|
||||||
|
/*
|
||||||
|
A toast the user must dismiss manually, because they need to act on its information,
|
||||||
|
ie., 8 bulk add genes out of 40 were bad. Manually see which ones and fix.
|
||||||
|
*/
|
||||||
|
export const keepAroundErrorToast = message =>
|
||||||
|
ErrorToastTopCenter.show({ message, timeout: 0, intent: Intent.WARNING });
|
||||||
|
|
||||||
/*
|
/*
|
||||||
a hard network error
|
a hard network error
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,12 +6,21 @@ import _ from "lodash";
|
|||||||
import fuzzysort from "fuzzysort";
|
import fuzzysort from "fuzzysort";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { MenuItem, Button } from "@blueprintjs/core";
|
import {
|
||||||
|
MenuItem,
|
||||||
|
Button,
|
||||||
|
FormGroup,
|
||||||
|
InputGroup,
|
||||||
|
ControlGroup
|
||||||
|
} from "@blueprintjs/core";
|
||||||
import { Suggest } from "@blueprintjs/select";
|
import { Suggest } from "@blueprintjs/select";
|
||||||
import HistogramBrush from "../brushableHistogram";
|
import HistogramBrush from "../brushableHistogram";
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
import actions from "../../actions";
|
import actions from "../../actions";
|
||||||
import { postUserErrorToast } from "../framework/toasters";
|
import {
|
||||||
|
postUserErrorToast,
|
||||||
|
keepAroundErrorToast
|
||||||
|
} from "../framework/toasters";
|
||||||
import ExpressionButtons from "./expressionButtons";
|
import ExpressionButtons from "./expressionButtons";
|
||||||
import finiteExtent from "../../util/finiteExtent";
|
import finiteExtent from "../../util/finiteExtent";
|
||||||
|
|
||||||
@@ -67,6 +76,14 @@ const filterGenes = (query, genes) => {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
class GeneExpression extends React.Component {
|
class GeneExpression extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
bulkAdd: "",
|
||||||
|
tab: "autosuggest"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
handleClick(g) {
|
handleClick(g) {
|
||||||
const { world, dispatch, userDefinedGenes } = this.props;
|
const { world, dispatch, userDefinedGenes } = this.props;
|
||||||
const gene = g.target;
|
const gene = g.target;
|
||||||
@@ -87,6 +104,29 @@ class GeneExpression extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleBulkAddClick() {
|
||||||
|
const { world, dispatch, userDefinedGenes } = this.props;
|
||||||
|
const { bulkAdd } = this.state;
|
||||||
|
|
||||||
|
const genes = _.uniq(bulkAdd.split(", ").map(g => g.trim()));
|
||||||
|
|
||||||
|
genes.forEach(gene => {
|
||||||
|
if (userDefinedGenes.indexOf(gene) !== -1) {
|
||||||
|
keepAroundErrorToast("That gene already exists");
|
||||||
|
} else if (!_.find(world.varAnnotations, { name: gene })) {
|
||||||
|
keepAroundErrorToast(`${gene} doesn't appear to be a valid gene name.`);
|
||||||
|
} else {
|
||||||
|
dispatch(actions.requestUserDefinedGene(gene));
|
||||||
|
dispatch({
|
||||||
|
type: "user defined gene",
|
||||||
|
data: gene
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({ bulkAdd: "" });
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
world,
|
world,
|
||||||
@@ -95,6 +135,8 @@ class GeneExpression extends React.Component {
|
|||||||
differential
|
differential
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { tab, bulkAdd } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@@ -111,39 +153,108 @@ class GeneExpression extends React.Component {
|
|||||||
Selected Genes
|
Selected Genes
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
style={{ padding: globals.leftSidebarSectionPadding }}
|
style={{
|
||||||
className="bp3-control-group"
|
padding: globals.leftSidebarSectionPadding
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Suggest
|
|
||||||
disabled={true}
|
|
||||||
closeOnSelect
|
|
||||||
openOnKeyDown
|
|
||||||
resetOnSelect
|
|
||||||
itemDisabled={userDefinedGenesLoading ? () => true : () => false}
|
|
||||||
noResults={<MenuItem disabled text="No matching genes." />}
|
|
||||||
onItemSelect={g => {
|
|
||||||
/* this happens on 'enter' */
|
|
||||||
this.handleClick(g);
|
|
||||||
}}
|
|
||||||
inputValueRenderer={g => {
|
|
||||||
return "";
|
|
||||||
}}
|
|
||||||
itemListPredicate={filterGenes}
|
|
||||||
itemRenderer={renderGene.bind(this)}
|
|
||||||
items={
|
|
||||||
world && world.varAnnotations
|
|
||||||
? world.varAnnotations
|
|
||||||
: [{ name: "No genes", n_counts: "" }]
|
|
||||||
}
|
|
||||||
popoverProps={{ minimal: true }}
|
|
||||||
/>
|
|
||||||
<Button
|
<Button
|
||||||
className="bp3-button bp3-intent-primary"
|
active={tab === "autosuggest"}
|
||||||
loading={userDefinedGenesLoading}
|
style={{ marginRight: 5 }}
|
||||||
|
minimal
|
||||||
|
small
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ tab: "autosuggest" });
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Add
|
Autosuggest
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
active={tab === "bulkadd"}
|
||||||
|
minimal
|
||||||
|
small
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ tab: "bulkadd" });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Bulk add genes
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{tab === "autosuggest" ? (
|
||||||
|
<ControlGroup
|
||||||
|
style={{
|
||||||
|
paddingLeft: globals.leftSidebarSectionPadding,
|
||||||
|
paddingBottom: globals.leftSidebarSectionPadding
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Suggest
|
||||||
|
closeOnSelect
|
||||||
|
openOnKeyDown
|
||||||
|
resetOnSelect
|
||||||
|
itemDisabled={
|
||||||
|
userDefinedGenesLoading ? () => true : () => false
|
||||||
|
}
|
||||||
|
noResults={<MenuItem disabled text="No matching genes." />}
|
||||||
|
onItemSelect={g => {
|
||||||
|
/* this happens on 'enter' */
|
||||||
|
this.handleClick(g);
|
||||||
|
}}
|
||||||
|
inputValueRenderer={g => {
|
||||||
|
return "";
|
||||||
|
}}
|
||||||
|
itemListPredicate={filterGenes}
|
||||||
|
itemRenderer={renderGene.bind(this)}
|
||||||
|
items={
|
||||||
|
world && world.varAnnotations
|
||||||
|
? world.varAnnotations
|
||||||
|
: [{ name: "No genes" }]
|
||||||
|
}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="bp3-button bp3-intent-primary"
|
||||||
|
loading={userDefinedGenesLoading}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</ControlGroup>
|
||||||
|
) : null}
|
||||||
|
{tab === "bulkadd" ? (
|
||||||
|
<div style={{ paddingLeft: globals.leftSidebarSectionPadding }}>
|
||||||
|
<form
|
||||||
|
onSubmit={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.handleBulkAddClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormGroup
|
||||||
|
helperText="Add a list of genes (comma delimited)"
|
||||||
|
labelFor="text-input-bulk-add"
|
||||||
|
onSubmit={() => {
|
||||||
|
console.log("heyo");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ControlGroup>
|
||||||
|
<InputGroup
|
||||||
|
onChange={e => {
|
||||||
|
this.setState({ bulkAdd: e.target.value });
|
||||||
|
}}
|
||||||
|
id="text-input-bulk-add"
|
||||||
|
placeholder="Apod, Cd74, ..."
|
||||||
|
value={bulkAdd}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
intent="primary"
|
||||||
|
onClick={this.handleBulkAddClick.bind(this)}
|
||||||
|
loading={userDefinedGenesLoading}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</ControlGroup>
|
||||||
|
</FormGroup>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{world && userDefinedGenes.length > 0
|
{world && userDefinedGenes.length > 0
|
||||||
? _.map(userDefinedGenes, (geneName, index) => {
|
? _.map(userDefinedGenes, (geneName, index) => {
|
||||||
const values = world.varDataCache[geneName];
|
const values = world.varDataCache[geneName];
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Colors } from "@blueprintjs/core";
|
||||||
|
|
||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
/* these will be either (preferably) specified or inferred */
|
/* these will be either (preferably) specified or inferred */
|
||||||
export const categories = [
|
export const categories = [
|
||||||
@@ -44,8 +46,8 @@ export const configDefaults = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* colors */
|
/* colors */
|
||||||
export const blue = "#4a90e2";
|
export const blue = Colors.BLUE3;
|
||||||
export const hcaBlue = "#1c7cc7";
|
export const linkBlue = Colors.BLUE5;
|
||||||
export const lightestGrey = "rgb(249,249,249)";
|
export const lightestGrey = "rgb(249,249,249)";
|
||||||
export const lighterGrey = "rgb(245,245,245)";
|
export const lighterGrey = "rgb(245,245,245)";
|
||||||
export const lightGrey = "rgb(211,211,211)";
|
export const lightGrey = "rgb(211,211,211)";
|
||||||
|
|||||||
Reference in New Issue
Block a user