mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-29 18:38:11 +08:00
Improve label picking (#1179)
* add simple error message helper * port all label name pickers to use the new LabelInput component * use pure components where possible * cleanup * more cleanup * lint * change new label prompt
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
// jshint esversion: 6
|
||||
import { connect } from "react-redux";
|
||||
import React from "react";
|
||||
|
||||
@@ -9,17 +8,16 @@ import {
|
||||
Popover,
|
||||
Position,
|
||||
PopoverInteractionKind,
|
||||
Tooltip,
|
||||
Colors
|
||||
Tooltip
|
||||
} from "@blueprintjs/core";
|
||||
import Occupancy from "./occupancy";
|
||||
import * as globals from "../../globals";
|
||||
import styles from "./categorical.css";
|
||||
import AnnoDialog from "./annoDialog";
|
||||
import AnnoInputs from "./annoInputs";
|
||||
import LabelInput from "./labelInput";
|
||||
|
||||
import { AnnotationsHelpers } from "../../util/stateManager";
|
||||
import { labelErrorMessage, isLabelErroneous } from "./labelUtil";
|
||||
import { labelPrompt, isLabelErroneous } from "./labelUtil";
|
||||
|
||||
@connect(state => ({
|
||||
categoricalSelection: state.categoricalSelection,
|
||||
@@ -30,19 +28,13 @@ import { labelErrorMessage, isLabelErroneous } from "./labelUtil";
|
||||
schema: state.world?.schema,
|
||||
world: state.world,
|
||||
crossfilter: state.crossfilter,
|
||||
ontology: state.ontology,
|
||||
ontologyLoading: state.ontology?.loading,
|
||||
ontologyEnabled: state.ontology?.enabled
|
||||
ontology: state.ontology
|
||||
}))
|
||||
class CategoryValue extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
editedLabelText: String(
|
||||
props.categoricalSelection[props.metadataField].categoryValues[
|
||||
props.categoryIndex
|
||||
]
|
||||
).valueOf()
|
||||
editedLabelText: this.currentLabel()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,9 +46,7 @@ class CategoryValue extends React.Component {
|
||||
prevProps.categoryIndex !== categoryIndex
|
||||
) {
|
||||
this.setState({
|
||||
editedLabelText: String(
|
||||
categoricalSelection[metadataField].categoryValues[categoryIndex]
|
||||
).valueOf()
|
||||
editedLabelText: this.currentLabel()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -98,49 +88,27 @@ class CategoryValue extends React.Component {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
handleCreateArbitraryLabel = editedLabelTextNotInOntology => {
|
||||
handleCreateArbitraryLabel = txt => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const label = this.getLabel();
|
||||
this.cancelEditMode();
|
||||
dispatch({
|
||||
type: "annotation: label edited",
|
||||
metadataField,
|
||||
editedLabel: editedLabelTextNotInOntology,
|
||||
editedLabel: txt,
|
||||
categoryIndex,
|
||||
label
|
||||
});
|
||||
};
|
||||
|
||||
labelNameError = name => {
|
||||
const {
|
||||
metadataField,
|
||||
ontology,
|
||||
schema,
|
||||
categoricalSelection,
|
||||
categoryIndex
|
||||
} = this.props;
|
||||
const category = categoricalSelection[metadataField];
|
||||
const displayString = String(
|
||||
category.categoryValues[categoryIndex]
|
||||
).valueOf();
|
||||
if (name === displayString) return false;
|
||||
const { metadataField, ontology, schema } = this.props;
|
||||
if (name === this.currentLabel()) return false;
|
||||
return isLabelErroneous(name, metadataField, ontology, schema);
|
||||
};
|
||||
|
||||
labelNameErrorMessage = name => {
|
||||
const {
|
||||
metadataField,
|
||||
ontology,
|
||||
schema,
|
||||
categoricalSelection,
|
||||
categoryIndex
|
||||
} = this.props;
|
||||
const category = categoricalSelection[metadataField];
|
||||
const displayString = String(
|
||||
category.categoryValues[categoryIndex]
|
||||
).valueOf();
|
||||
if (name === displayString) return null;
|
||||
return labelErrorMessage(name, metadataField, ontology, schema);
|
||||
instruction = label => {
|
||||
return labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
};
|
||||
|
||||
activateEditLabelMode = () => {
|
||||
@@ -154,6 +122,9 @@ class CategoryValue extends React.Component {
|
||||
|
||||
cancelEditMode = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabel()
|
||||
});
|
||||
dispatch({
|
||||
type: "annotation: cancel edit label mode",
|
||||
metadataField,
|
||||
@@ -255,6 +226,13 @@ class CategoryValue extends React.Component {
|
||||
return label;
|
||||
};
|
||||
|
||||
currentLabel() {
|
||||
const { categoricalSelection, metadataField, categoryIndex } = this.props;
|
||||
return String(
|
||||
categoricalSelection[metadataField].categoryValues[categoryIndex]
|
||||
).valueOf();
|
||||
}
|
||||
|
||||
isAddCurrentSelectionDisabled(category, value) {
|
||||
/*
|
||||
disable "add current selection to label", if one of the following is true:
|
||||
@@ -294,12 +272,13 @@ class CategoryValue extends React.Component {
|
||||
schema,
|
||||
isUserAnno,
|
||||
annotations,
|
||||
ontologyEnabled,
|
||||
ontology,
|
||||
// flippedProps is potentially brittle, their docs want {...flippedProps} on our div,
|
||||
// our lint doesn't like jsx spread, we are version pinned to prevent api change on their part
|
||||
flippedProps,
|
||||
pointDilation
|
||||
} = this.props;
|
||||
const ontologyEnabled = ontology?.enabled ?? false;
|
||||
|
||||
const { editedLabelText } = this.state;
|
||||
|
||||
@@ -309,9 +288,7 @@ class CategoryValue extends React.Component {
|
||||
const selected = category.categoryValueSelected[categoryIndex];
|
||||
const count = category.categoryValueCounts[categoryIndex];
|
||||
const value = category.categoryValues[categoryIndex];
|
||||
const displayString = String(
|
||||
category.categoryValues[categoryIndex]
|
||||
).valueOf();
|
||||
const displayString = this.currentLabel();
|
||||
|
||||
/* this is the color scale, so add swatches below */
|
||||
const isColorBy = metadataField === colorAccessor;
|
||||
@@ -442,30 +419,26 @@ class CategoryValue extends React.Component {
|
||||
"data-testid": `${metadataField}:${displayString}:submit-label-edit`
|
||||
}}
|
||||
title="Edit label"
|
||||
instruction={`New label text must be unique within category ${metadataField}:`}
|
||||
instruction={this.instruction(editedLabelText)}
|
||||
cancelTooltipContent="Close this dialog without editing label text."
|
||||
primaryButtonText="Change label text"
|
||||
text={editedLabelText}
|
||||
categoryToDuplicate={null}
|
||||
validationError={this.labelNameError(editedLabelText)}
|
||||
errorMessage={this.labelNameErrorMessage(editedLabelText)}
|
||||
handleSubmit={this.handleEditValue}
|
||||
handleCancel={this.cancelEditMode}
|
||||
annoInput={
|
||||
<AnnoInputs
|
||||
useSuggest={ontologyEnabled}
|
||||
text={editedLabelText}
|
||||
<LabelInput
|
||||
label={editedLabelText}
|
||||
labelSuggestions={ontologyEnabled ? ontology.terms : null}
|
||||
onChange={this.handleTextChange}
|
||||
onSelect={this.handleTextChange}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:${displayString}:edit-label-name`
|
||||
"data-testid": `${metadataField}:${displayString}:edit-label-name`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true
|
||||
}}
|
||||
handleCreateArbitraryLabel={
|
||||
this.handleCreateArbitraryLabel
|
||||
}
|
||||
handleItemChange={this.handleSuggestActiveItemChange}
|
||||
handleChoice={this.handleChoice}
|
||||
handleTextChange={this.handleTextChange}
|
||||
isTextInvalid={this.labelNameError}
|
||||
isTextInvalidErrorMessage={this.labelNameErrorMessage}
|
||||
/>
|
||||
}
|
||||
annoSelect={null}
|
||||
@@ -475,7 +448,10 @@ class CategoryValue extends React.Component {
|
||||
</div>
|
||||
<span style={{ flexShrink: 0 }}>
|
||||
{colorAccessor && !isColorBy && !annotations.isEditingLabelName ? (
|
||||
<Occupancy category={category} {...this.props} />
|
||||
<Occupancy
|
||||
category={category}
|
||||
{...this.props} // eslint-disable-line react/jsx-props-no-spreading
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user