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:
Bruce Martin
2020-02-28 15:46:40 -07:00
committed by GitHub
parent 1e4381ab7f
commit bf7d7342d5
27 changed files with 418 additions and 576 deletions
+39 -63
View File
@@ -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>