Create / edit geneset description (#2139)

* geneset description add

* edit geneset description

* default state for desc

* remove log

* naming, todo

* check for both dup name and desc

* fixes

* do not store gene set modal state in history stack

* Update createGenesetDialogue.js

* Update editGenesetNameDialogue.js

Co-authored-by: bkmartinjr <bruce@chanzuckerberg.com>
This commit is contained in:
Colin Megill
2021-04-21 19:50:29 -04:00
committed by GitHub
co-authored by bkmartinjr
parent 99a795a688
commit 876ceb4d8b
9 changed files with 170 additions and 69 deletions
+1 -1
View File
@@ -276,7 +276,7 @@ describe("geneset: update", () => {
},
}
);
}).toThrow("update specified existing name");
}).toThrow("update specified existing name and description");
});
});
+2
View File
@@ -48,6 +48,7 @@ class AnnoDialog extends React.PureComponent {
>
{errorMessage}
</p>
{/* we might rename, secondary button and secondary input are not related */}
{secondaryInstructions && (
<p style={{ marginTop: secondaryInstructions ? 20 : 0 }}>
{secondaryInstructions}
@@ -62,6 +63,7 @@ class AnnoDialog extends React.PureComponent {
<Tooltip content={cancelTooltipContent}>
<Button onClick={handleCancel}>Cancel</Button>
</Tooltip>
{/* we might rename, secondary button and secondary input are not related */}
{handleSecondaryButtonSubmit && secondaryButtonText ? (
<Button
onClick={handleSecondaryButtonSubmit}
@@ -1,6 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import { Tooltip, Position, Switch } from "@blueprintjs/core";
import { Position, Switch } from "@blueprintjs/core";
import { Tooltip2 } from "@blueprintjs/popover2";
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
import actions from "../../actions";
import Gene from "./gene";
@@ -128,7 +129,7 @@ class GeneSet extends React.Component {
}
render() {
const { setName, setGenes } = this.props;
const { setName, setGenes, genesetDescription } = this.props;
const { isOpen, toggleSummaryHisto } = this.state;
const genesetNameLengthVisible = 120; /* this magic number determines how much of a long geneset name we see */
const genesetIsEmpty = setGenes.length === 0;
@@ -156,7 +157,11 @@ class GeneSet extends React.Component {
}}
onClick={this.onGenesetMenuClick}
>
<Truncate>
<Truncate
tooltipAddendum={
genesetDescription ? `: ${genesetDescription}` : ""
}
>
<span
style={{
maxWidth: globals.leftSidebarWidth - genesetNameLengthVisible,
@@ -185,7 +190,7 @@ class GeneSet extends React.Component {
<div style={{ marginLeft: 15, marginTop: 5, marginRight: 0 }}>
{isOpen && !genesetIsEmpty && (
<Tooltip
<Tooltip2
content="Aggregate all genes in this geneset, and allow coloring by and selecting on the entire set."
position={Position.BOTTOM_RIGHT}
usePortal
@@ -204,7 +209,7 @@ class GeneSet extends React.Component {
innerLabelChecked="on"
onChange={this.toggleSummaryHisto}
/>
</Tooltip>
</Tooltip2>
)}
{isOpen && genesetIsEmpty && (
@@ -223,7 +228,10 @@ class GeneSet extends React.Component {
setGenes={setGenes}
/>
))}
<EditGenesetNameDialogue parentGeneset={setName} />
<EditGenesetNameDialogue
parentGeneset={setName}
parentGenesetDescription={genesetDescription}
/>
</div>
);
}
@@ -23,6 +23,7 @@ class GeneExpression extends React.Component {
setGenes={Array.from(geneset.genes.keys())}
setGenesWithDescriptions={geneset.genes}
setName={name}
genesetDescription={geneset.genesetDescription}
/>
);
}
@@ -2,7 +2,8 @@ import pull from "lodash.pull";
import uniq from "lodash.uniq";
import React from "react";
import { connect } from "react-redux";
import AnnoDialog from "../../annoDialog";
import { Button, Dialog, Classes, Colors } from "@blueprintjs/core";
import { Tooltip2 } from "@blueprintjs/popover2";
import LabelInput from "../../labelInput";
import actions from "../../../actions";
@@ -20,6 +21,7 @@ class CreateGenesetDialogue extends React.PureComponent {
this.state = {
genesetName: "",
genesToPopulateGeneset: "",
genesetDescription: "",
};
}
@@ -28,6 +30,7 @@ class CreateGenesetDialogue extends React.PureComponent {
this.setState({
genesetName: "",
genesToPopulateGeneset: "",
genesetDescription: "",
});
dispatch({
type: "geneset: disable create geneset mode",
@@ -37,12 +40,16 @@ class CreateGenesetDialogue extends React.PureComponent {
createGeneset = (e) => {
const { dispatch } = this.props;
const { genesetName, genesToPopulateGeneset } = this.state;
const {
genesetName,
genesToPopulateGeneset,
genesetDescription,
} = this.state;
dispatch({
type: "geneset: create",
genesetName,
genesetDescription: "",
genesetDescription,
});
if (genesToPopulateGeneset) {
const genesTmpHardcodedFormat = [];
@@ -82,10 +89,14 @@ class CreateGenesetDialogue extends React.PureComponent {
this.setState({ genesToPopulateGeneset: e });
};
handleDescriptionInputChange = (e) => {
this.setState({ genesetDescription: e });
};
instruction = (genesetName, genesets) => {
return genesets.has(genesetName)
? "Geneset name must be unique."
: "New, unique geneset name";
? "Gene set name must be unique."
: "New, unique gene set name";
};
validate = (genesetName, genesets) => {
@@ -98,47 +109,94 @@ class CreateGenesetDialogue extends React.PureComponent {
return (
<>
<AnnoDialog
isActive={genesetsUI.createGenesetModeActive}
inputProps={{
"data-testid": `${metadataField}:create-geneset-dialog`,
}}
primaryButtonProps={{
"data-testid": `${metadataField}:submit-geneset`,
}}
<Dialog
icon="tag"
title="Create gene set"
instruction={this.instruction(genesetName, genesets)}
cancelTooltipContent="Close this dialog without creating a new gene set."
primaryButtonText="Create gene set"
text={genesetName}
validationError={this.validate(genesetName, genesets)}
annoInput={
<LabelInput
onChange={this.handleChange}
inputProps={{
"data-testid": "create-geneset-modal",
leftIcon: "manually-entered-data",
intent: "none",
autoFocus: true,
}}
newLabelMessage="Create gene set"
/>
}
secondaryInstructions="Optionally add a list of comma separated genes to populate the gene set"
secondaryInput={
<LabelInput
onChange={this.handleGenesetInputChange}
inputProps={{
"data-testid": "add-genes",
intent: "none",
autoFocus: false,
}}
newLabelMessage="populate geneset with genes"
/>
}
handleSubmit={this.createGeneset}
handleCancel={this.disableCreateGenesetMode}
/>
isOpen={genesetsUI.createGenesetModeActive}
onClose={this.disableCreateGenesetMode}
>
<form
onSubmit={(e) => {
e.preventDefault();
}}
>
<div className={Classes.DIALOG_BODY}>
<div style={{ marginBottom: 20 }}>
<p>{this.instruction(genesetName, genesets)}</p>
<LabelInput
onChange={this.handleChange}
inputProps={{
"data-testid": "create-geneset-modal",
leftIcon: "manually-entered-data",
intent: "none",
autoFocus: true,
}}
newLabelMessage="Create gene set"
/>
<p
style={{
marginTop: 7,
visibility: this.validate(genesetName, genesets)
? "visible"
: "hidden",
color: Colors.ORANGE3,
}}
>
{this.genesetNameError()}
</p>
<p style={{ marginTop: 20 }}>
Optionally add a{" "}
<span style={{ fontWeight: 700 }}>description</span> for this
gene set
</p>
<LabelInput
onChange={this.handleDescriptionInputChange}
inputProps={{
"data-testid": "add-geneset-description",
intent: "none",
autoFocus: false,
}}
newLabelMessage="Add geneset description"
/>
<p style={{ marginTop: 20 }}>
Optionally add a list of comma separated{" "}
<span style={{ fontWeight: 700 }}>genes</span> to populate the
gene set
</p>
<LabelInput
onChange={this.handleGenesetInputChange}
inputProps={{
"data-testid": "add-genes",
intent: "none",
autoFocus: false,
}}
newLabelMessage="populate geneset with genes"
/>
</div>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Tooltip2 content="Close this dialog without creating a new gene set.">
<Button onClick={this.disableCreateGenesetMode}>
Cancel
</Button>
</Tooltip2>
<Button
data-testid={`${metadataField}:submit-geneset`}
onClick={this.createGeneset}
disabled={
!genesetName || this.validate(genesetName, genesets)
}
intent="primary"
type="submit"
>
Create gene set
</Button>
</div>
</div>
</form>
</Dialog>
</>
);
}
@@ -14,14 +14,16 @@ class RenameGeneset extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
genesetName: props.parentGeneset,
newGenesetName: props.parentGeneset,
newGenesetDescription: props.parentGenesetDescription,
};
}
disableEditGenesetNameMode = (e) => {
const { dispatch } = this.props;
this.setState({
genesetName: "",
newGenesetName: "",
newGenesetDescription: "",
});
dispatch({
type: "geneset: disable rename geneset mode",
@@ -31,12 +33,15 @@ class RenameGeneset extends React.PureComponent {
renameGeneset = (e) => {
const { dispatch, genesetsUI } = this.props;
const { genesetName } = this.state;
const { newGenesetName, newGenesetDescription } = this.state;
dispatch({
type: "geneset: update",
genesetName: genesetsUI.isEditingGenesetName,
update: { genesetName },
update: {
genesetName: newGenesetName,
genesetDescription: newGenesetDescription,
},
});
dispatch({
type: "geneset: disable rename geneset mode",
@@ -49,12 +54,16 @@ class RenameGeneset extends React.PureComponent {
};
handleChange = (e) => {
this.setState({ genesetName: e });
this.setState({ newGenesetName: e });
};
handleChangeDescription = (e) => {
this.setState({ newGenesetDescription: e });
};
render() {
const { genesetName } = this.state;
const { genesetsUI, parentGeneset } = this.props;
const { newGenesetName, newGenesetDescription } = this.state;
const { genesetsUI, parentGeneset, parentGenesetDescription } = this.props;
return (
<>
@@ -66,15 +75,19 @@ class RenameGeneset extends React.PureComponent {
primaryButtonProps={{
"data-testid": `${genesetsUI.isEditingGenesetName}:submit-geneset`,
}}
title="Rename gene set"
title="Edit gene set name and description"
instruction={`Rename ${genesetsUI.isEditingGenesetName}`}
cancelTooltipContent="Close this dialog without renaming the gene set."
primaryButtonText="Rename gene set"
text={genesetName}
validationError={genesetsUI.isEditingGenesetName === genesetName}
primaryButtonText="Edit gene set name and description"
text={newGenesetName}
secondaryText={newGenesetDescription}
validationError={
genesetsUI.isEditingGenesetName === newGenesetName &&
parentGenesetDescription === newGenesetDescription
}
annoInput={
<LabelInput
label={genesetName}
label={newGenesetName}
onChange={this.handleChange}
inputProps={{
"data-testid": "rename-geneset-modal",
@@ -82,7 +95,16 @@ class RenameGeneset extends React.PureComponent {
intent: "none",
autoFocus: true,
}}
newLabelMessage="Rename gene set"
/>
}
secondaryInstructions="Edit description"
secondaryInput={
<LabelInput
label={newGenesetDescription}
onChange={this.handleChangeDescription}
inputProps={{ "data-testid": "change geneset description" }}
intent="none"
autoFocus={false}
/>
}
handleSubmit={this.renameGeneset}
@@ -82,7 +82,7 @@ class GenesetMenus extends React.PureComponent {
data-testclass="activateEditGenesetNameMode"
data-testid={`${geneset}:edit-genesetName-mode`}
onClick={this.activateEditGenesetNameMode}
text="Edit gene set name"
text="Edit gene set name and description"
/>
<MenuItem
icon="trash"
+13 -2
View File
@@ -149,6 +149,7 @@ const GeneSets = (
*/
case "geneset: update": {
const { genesetName, update } = action;
if (
typeof genesetName !== "string" ||
!genesetName ||
@@ -157,8 +158,18 @@ const GeneSets = (
throw new Error(
"geneset: update -- geneset name unspecified or does not exist."
);
if (state.genesets.has(update.genesetName))
throw new Error("geneset: update -- update specified existing name.");
/* now that we've confirmed the gene set exists, check for duplicates */
const genesetNameIsDuplicate = state.genesets.has(update.genesetName);
const descriptionIsDuplicate =
state.genesets.get(update.genesetName) &&
state.genesets.get(update.genesetName).genesetDescription ===
update.genesetDescription;
if (genesetNameIsDuplicate && descriptionIsDuplicate)
throw new Error(
"geneset: update -- update specified existing name and description."
);
const prevGs = state.genesets.get(genesetName);
const newGs = {
-1
View File
@@ -60,7 +60,6 @@ const Reducer = undoable(
"layoutChoice",
"centroidLabels",
"genesets",
"genesetsUI",
"annotations",
],
undoableConfig