feat: frontend geneset validation (#2258)

* add geneset name validation

* validate genes before addition

* display error messages
This commit is contained in:
Severiano Badajoz
2021-07-01 22:09:50 +00:00
committed by GitHub
parent b8b1d0dd9e
commit b714c18e75
4 changed files with 67 additions and 15 deletions
+19 -2
View File
@@ -1,3 +1,4 @@
import { postUserErrorToast } from "../components/framework/toasters";
/* /*
Action creators for gene sets Action creators for gene sets
@@ -39,9 +40,25 @@ export const genesetDelete = (genesetName) => (dispatch, getState) => {
}); });
}; };
export const genesetAddGenes = (genesetName, genes) => (dispatch, getState) => { export const genesetAddGenes = (genesetName, genes) => async (
dispatch,
getState
) => {
const state = getState(); const state = getState();
const { obsCrossfilter: prevObsCrossfilter } = state; const { obsCrossfilter: prevObsCrossfilter, annoMatrix } = state;
const { schema } = annoMatrix;
const varIndex = schema.annotations.var.index;
const df = await annoMatrix.fetch("var", varIndex);
const geneNames = df.col(varIndex).asArray();
genes = genes.reduce((acc, gene) => {
if (geneNames.indexOf(gene.geneSymbol) === -1) {
postUserErrorToast(
`${gene.geneSymbol} doesn't appear to be a valid gene name.`
);
} else acc.push(gene);
return acc;
}, []);
const obsCrossfilter = dropGenesetSummaryDimension( const obsCrossfilter = dropGenesetSummaryDimension(
prevObsCrossfilter, prevObsCrossfilter,
state, state,
@@ -61,7 +61,7 @@ class AddGeneToGenesetDialogue extends React.PureComponent {
"data-testid": `${geneset}:submit-label`, "data-testid": `${geneset}:submit-label`,
}} }}
title="Add genes to gene set" title="Add genes to gene set"
instruction={`Add gene to ${geneset}`} instruction={`Add genes to ${geneset}`}
cancelTooltipContent="Close this dialog without adding genes to gene set." cancelTooltipContent="Close this dialog without adding genes to gene set."
primaryButtonText="Add genes" primaryButtonText="Add genes"
text={genesToAdd} text={genesToAdd}
@@ -31,6 +31,7 @@ class CreateGenesetDialogue extends React.PureComponent {
genesetName: "", genesetName: "",
genesToPopulateGeneset: "", genesToPopulateGeneset: "",
genesetDescription: "", genesetDescription: "",
nameErrorMessage: "",
}); });
dispatch({ dispatch({
type: "geneset: disable create geneset mode", type: "geneset: disable create geneset mode",
@@ -48,7 +49,7 @@ class CreateGenesetDialogue extends React.PureComponent {
dispatch({ dispatch({
type: "geneset: create", type: "geneset: create",
genesetName, genesetName: genesetName.trim(),
genesetDescription, genesetDescription,
}); });
if (genesToPopulateGeneset) { if (genesToPopulateGeneset) {
@@ -82,7 +83,9 @@ class CreateGenesetDialogue extends React.PureComponent {
}; };
handleChange = (e) => { handleChange = (e) => {
const { genesets } = this.props;
this.setState({ genesetName: e }); this.setState({ genesetName: e });
this.validate(e, genesets);
}; };
handleGenesetInputChange = (e) => { handleGenesetInputChange = (e) => {
@@ -100,11 +103,32 @@ class CreateGenesetDialogue extends React.PureComponent {
}; };
validate = (genesetName, genesets) => { validate = (genesetName, genesets) => {
return genesets.has(genesetName); if (genesets.has(genesetName)) {
this.setState({
nameErrorMessage: "There is already a geneset with that name",
});
return false;
}
if (
genesetName.length > 1 &&
// eslint-disable-next-line no-control-regex -- unicode 0-31 127-65535
genesetName.match(/^[\u0000-\u001F\u007F-\uFFFF]|[ ]{2,}/g)?.length
) {
this.setState({
nameErrorMessage:
"Gene set names can only contain alphanumeric characters and the following special characters: ! ” # $ % ’ ( ) * + , - . / : ; < = > ? @ ] ^ _ ` | ~",
});
return false;
}
this.setState({
nameErrorMessage: "",
});
return true;
}; };
render() { render() {
const { genesetName } = this.state; const { genesetName, nameErrorMessage } = this.state;
const { metadataField, genesetsUI, genesets } = this.props; const { metadataField, genesetsUI, genesets } = this.props;
return ( return (
@@ -136,13 +160,11 @@ class CreateGenesetDialogue extends React.PureComponent {
<p <p
style={{ style={{
marginTop: 7, marginTop: 7,
visibility: this.validate(genesetName, genesets) visibility: nameErrorMessage !== "" ? "visible" : "hidden",
? "visible"
: "hidden",
color: Colors.ORANGE3, color: Colors.ORANGE3,
}} }}
> >
{this.genesetNameError()} {nameErrorMessage}
</p> </p>
<p style={{ marginTop: 20 }}> <p style={{ marginTop: 20 }}>
Optionally add a{" "} Optionally add a{" "}
@@ -185,9 +207,7 @@ class CreateGenesetDialogue extends React.PureComponent {
<Button <Button
data-testid={`${metadataField}:submit-geneset`} data-testid={`${metadataField}:submit-geneset`}
onClick={this.createGeneset} onClick={this.createGeneset}
disabled={ disabled={nameErrorMessage !== ""}
!genesetName || this.validate(genesetName, genesets)
}
intent="primary" intent="primary"
type="submit" type="submit"
> >
@@ -9,6 +9,7 @@ import LabelInput from "../../labelInput";
ontology: state.ontology, ontology: state.ontology,
obsCrossfilter: state.obsCrossfilter, obsCrossfilter: state.obsCrossfilter,
genesetsUI: state.genesetsUI, genesetsUI: state.genesetsUI,
genesets: state.genesets.genesets,
})) }))
class RenameGeneset extends React.PureComponent { class RenameGeneset extends React.PureComponent {
constructor(props) { constructor(props) {
@@ -61,9 +62,23 @@ class RenameGeneset extends React.PureComponent {
this.setState({ newGenesetDescription: e }); this.setState({ newGenesetDescription: e });
}; };
validate = (genesetName, genesets) => {
return (
!genesets.has(genesetName) &&
// eslint-disable-next-line no-control-regex -- unicode 0-31 127-65535
genesetName.match(/^\s|[\u0000-\u001F\u007F-\uFFFF]|[ ]{2,}|^$|\s$/g)
?.length
);
};
render() { render() {
const { newGenesetName, newGenesetDescription } = this.state; const { newGenesetName, newGenesetDescription } = this.state;
const { genesetsUI, parentGeneset, parentGenesetDescription } = this.props; const {
genesetsUI,
parentGeneset,
parentGenesetDescription,
genesets,
} = this.props;
return ( return (
<> <>
@@ -82,7 +97,7 @@ class RenameGeneset extends React.PureComponent {
text={newGenesetName} text={newGenesetName}
secondaryText={newGenesetDescription} secondaryText={newGenesetDescription}
validationError={ validationError={
genesetsUI.isEditingGenesetName === newGenesetName && this.validate(newGenesetName, genesets) &&
parentGenesetDescription === newGenesetDescription parentGenesetDescription === newGenesetDescription
} }
annoInput={ annoInput={