mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-22 07:38:12 +08:00
Handle loading state when rendering categories with one label (#1393)
* refactor & loading state * truncate * fix minor PR review issues * lint Co-authored-by: bkmartinjr <bruce@chanzuckerberg.com>
This commit is contained in:
co-authored by
bkmartinjr
parent
7d4d360e52
commit
ea434fc46f
+18
-18
@@ -1,37 +1,37 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import AnnoDialog from "./annoDialog";
|
||||
import LabelInput from "./labelInput";
|
||||
import { labelPrompt, isLabelErroneous } from "./labelUtil";
|
||||
import AnnoDialog from "../annoDialog";
|
||||
import LabelInput from "../labelInput";
|
||||
import { labelPrompt, isLabelErroneous } from "../labelUtil";
|
||||
|
||||
@connect(state => ({
|
||||
@connect((state) => ({
|
||||
colorAccessor: state.colors.colorAccessor,
|
||||
categoricalSelection: state.categoricalSelection,
|
||||
annotations: state.annotations,
|
||||
universe: state.universe,
|
||||
ontology: state.ontology,
|
||||
crossfilter: state.crossfilter
|
||||
crossfilter: state.crossfilter,
|
||||
}))
|
||||
class Category extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
newLabelText: ""
|
||||
newLabelText: "",
|
||||
};
|
||||
}
|
||||
|
||||
disableAddNewLabelMode = e => {
|
||||
disableAddNewLabelMode = (e) => {
|
||||
const { dispatch } = this.props;
|
||||
this.setState({
|
||||
newLabelText: ""
|
||||
newLabelText: "",
|
||||
});
|
||||
dispatch({
|
||||
type: "annotation: disable add new label mode"
|
||||
type: "annotation: disable add new label mode",
|
||||
});
|
||||
if (e) e.preventDefault();
|
||||
};
|
||||
|
||||
handleAddNewLabelToCategory = e => {
|
||||
handleAddNewLabelToCategory = (e) => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newLabelText } = this.state;
|
||||
|
||||
@@ -40,12 +40,12 @@ class Category extends React.PureComponent {
|
||||
type: "annotation: add new label to category",
|
||||
metadataField,
|
||||
newLabelText,
|
||||
assignSelectedCells: false
|
||||
assignSelectedCells: false,
|
||||
});
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
addLabelAndAssignCells = e => {
|
||||
addLabelAndAssignCells = (e) => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newLabelText } = this.state;
|
||||
|
||||
@@ -54,21 +54,21 @@ class Category extends React.PureComponent {
|
||||
type: "annotation: add new label to category",
|
||||
metadataField,
|
||||
newLabelText,
|
||||
assignSelectedCells: true
|
||||
assignSelectedCells: true,
|
||||
});
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
labelNameError = name => {
|
||||
labelNameError = (name) => {
|
||||
const { metadataField, ontology, universe } = this.props;
|
||||
return isLabelErroneous(name, metadataField, ontology, universe.schema);
|
||||
};
|
||||
|
||||
instruction = label => {
|
||||
instruction = (label) => {
|
||||
return labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
};
|
||||
|
||||
handleChangeOrSelect = label => {
|
||||
handleChangeOrSelect = (label) => {
|
||||
this.setState({ newLabelText: label });
|
||||
};
|
||||
|
||||
@@ -86,7 +86,7 @@ class Category extends React.PureComponent {
|
||||
}
|
||||
inputProps={{ "data-testid": `${metadataField}:create-label-dialog` }}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:submit-label`
|
||||
"data-testid": `${metadataField}:submit-label`,
|
||||
}}
|
||||
title="Add new label to category"
|
||||
instruction={this.instruction(newLabelText)}
|
||||
@@ -107,7 +107,7 @@ class Category extends React.PureComponent {
|
||||
"data-testid": `${metadataField}:new-label-name`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true
|
||||
autoFocus: true,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
+17
-17
@@ -1,41 +1,41 @@
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import AnnoDialog from "./annoDialog";
|
||||
import LabelInput from "./labelInput";
|
||||
import { labelPrompt } from "./labelUtil";
|
||||
import AnnoDialog from "../annoDialog";
|
||||
import LabelInput from "../labelInput";
|
||||
import { labelPrompt } from "../labelUtil";
|
||||
|
||||
import { AnnotationsHelpers } from "../../util/stateManager";
|
||||
import { AnnotationsHelpers } from "../../../util/stateManager";
|
||||
|
||||
@connect(state => ({
|
||||
@connect((state) => ({
|
||||
categoricalSelection: state.categoricalSelection,
|
||||
annotations: state.annotations,
|
||||
universe: state.universe,
|
||||
ontology: state.ontology
|
||||
ontology: state.ontology,
|
||||
}))
|
||||
class AnnoDialogEditCategoryName extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
newCategoryText: props.metadataField
|
||||
newCategoryText: props.metadataField,
|
||||
};
|
||||
}
|
||||
|
||||
handleChangeOrSelect = name => {
|
||||
handleChangeOrSelect = (name) => {
|
||||
this.setState({
|
||||
newCategoryText: name
|
||||
newCategoryText: name,
|
||||
});
|
||||
};
|
||||
|
||||
disableEditCategoryMode = () => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "annotation: disable category edit mode"
|
||||
type: "annotation: disable category edit mode",
|
||||
});
|
||||
this.setState({ newCategoryText: metadataField });
|
||||
};
|
||||
|
||||
handleEditCategory = e => {
|
||||
handleEditCategory = (e) => {
|
||||
const { dispatch, metadataField, categoricalSelection } = this.props;
|
||||
const { newCategoryText } = this.state;
|
||||
|
||||
@@ -54,12 +54,12 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
|
||||
type: "annotation: category edited",
|
||||
metadataField,
|
||||
newCategoryText,
|
||||
data: newCategoryText
|
||||
data: newCategoryText,
|
||||
});
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
editedCategoryNameError = name => {
|
||||
editedCategoryNameError = (name) => {
|
||||
const { metadataField, categoricalSelection } = this.props;
|
||||
|
||||
/* check for syntax errors in category name */
|
||||
@@ -80,7 +80,7 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
|
||||
return false;
|
||||
};
|
||||
|
||||
instruction = name => {
|
||||
instruction = (name) => {
|
||||
return labelPrompt(
|
||||
this.editedCategoryNameError(name),
|
||||
"New, unique category name",
|
||||
@@ -101,10 +101,10 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
|
||||
annotations.categoryBeingEdited === metadataField
|
||||
}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-category-name-dialog`
|
||||
"data-testid": `${metadataField}:edit-category-name-dialog`,
|
||||
}}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:submit-category-edit`
|
||||
"data-testid": `${metadataField}:submit-category-edit`,
|
||||
}}
|
||||
title="Edit category name"
|
||||
instruction={this.instruction(newCategoryText)}
|
||||
@@ -124,7 +124,7 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
|
||||
"data-testid": `${metadataField}:edit-category-name-text`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true
|
||||
autoFocus: true,
|
||||
}}
|
||||
newLabelMessage="New category"
|
||||
/>
|
||||
+9
-9
@@ -3,11 +3,11 @@ import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { Flipper, Flipped } from "react-flip-toolkit";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import Value from "./value";
|
||||
import * as globals from "../../../globals";
|
||||
import Value from "../value";
|
||||
|
||||
@connect(state => ({
|
||||
categoricalSelection: state.categoricalSelection
|
||||
@connect((state) => ({
|
||||
categoricalSelection: state.categoricalSelection,
|
||||
}))
|
||||
class Category extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -21,7 +21,7 @@ class Category extends React.Component {
|
||||
return _.map(optTuples, (tuple, i) => {
|
||||
return (
|
||||
<Flipped key={tuple[1]} flipId={tuple[1]}>
|
||||
{flippedProps => (
|
||||
{(flippedProps) => (
|
||||
<Value
|
||||
isUserAnno={isUserAnno}
|
||||
optTuples={optTuples}
|
||||
@@ -42,17 +42,17 @@ class Category extends React.Component {
|
||||
metadataField,
|
||||
categoricalSelection,
|
||||
children,
|
||||
isExpanded
|
||||
isExpanded,
|
||||
} = this.props;
|
||||
const { isTruncated } = categoricalSelection[metadataField];
|
||||
const cat = categoricalSelection[metadataField];
|
||||
const optTuples = [...cat.categoryValueIndices];
|
||||
const optTuplesAsKey = _.map(optTuples, t => t[0]).join(""); // animation
|
||||
const optTuplesAsKey = _.map(optTuples, (t) => t[0]).join(""); // animation
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: globals.maxControlsWidth
|
||||
maxWidth: globals.maxControlsWidth,
|
||||
}}
|
||||
data-testclass="category"
|
||||
data-testid={`category-${metadataField}`}
|
||||
@@ -61,7 +61,7 @@ class Category extends React.Component {
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline"
|
||||
alignItems: "baseline",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
+49
-29
@@ -7,27 +7,31 @@ import {
|
||||
Button,
|
||||
Tooltip,
|
||||
Icon,
|
||||
Position
|
||||
Position,
|
||||
} from "@blueprintjs/core";
|
||||
import CategoryFlipperLayout from "./categoryFlipperLayout";
|
||||
import AnnoMenu from "./annoMenuCategory";
|
||||
import AnnoDialogEditCategoryName from "./annoDialogEditCategoryName";
|
||||
import AnnoDialogAddLabel from "./annoDialogAddLabel";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import maybeTruncateString from "../../util/maybeTruncateString";
|
||||
import * as globals from "../../../globals";
|
||||
import maybeTruncateString from "../../../util/maybeTruncateString";
|
||||
|
||||
@connect(state => ({
|
||||
colorAccessor: state.colors.colorAccessor,
|
||||
categoricalSelection: state.categoricalSelection,
|
||||
annotations: state.annotations,
|
||||
universe: state.universe
|
||||
}))
|
||||
@connect((state, ownProps) => {
|
||||
const { metadataField } = ownProps;
|
||||
return {
|
||||
isColorAccessor: state.colors.colorAccessor === metadataField,
|
||||
categoricalSelection: state.categoricalSelection,
|
||||
annotations: state.annotations,
|
||||
universe: state.universe,
|
||||
schema: state.world?.schema,
|
||||
};
|
||||
})
|
||||
class Category extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isChecked: true
|
||||
isChecked: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +51,7 @@ class Category extends React.Component {
|
||||
cat.categoryValueSelected,
|
||||
(res, cond) => (cond ? res + 1 : res),
|
||||
0
|
||||
)
|
||||
),
|
||||
};
|
||||
if (categoryCount.selectedCatCount === categoryCount.totalCatCount) {
|
||||
/* everything is on, so not indeterminate */
|
||||
@@ -69,7 +73,7 @@ class Category extends React.Component {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "color by categorical metadata",
|
||||
colorAccessor: metadataField
|
||||
colorAccessor: metadataField,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -77,7 +81,7 @@ class Category extends React.Component {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter all of these",
|
||||
metadataField
|
||||
metadataField,
|
||||
});
|
||||
this.setState({ isChecked: true });
|
||||
}
|
||||
@@ -86,7 +90,7 @@ class Category extends React.Component {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter none of these",
|
||||
metadataField
|
||||
metadataField,
|
||||
});
|
||||
this.setState({ isChecked: false });
|
||||
}
|
||||
@@ -114,25 +118,25 @@ class Category extends React.Component {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: globals.maxControlsWidth
|
||||
maxWidth: globals.maxControlsWidth,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline"
|
||||
alignItems: "baseline",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start"
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label className="bp3-control bp3-checkbox">
|
||||
<input disabled checked={true} type="checkbox" />
|
||||
<input disabled checked type="checkbox" />
|
||||
<span className="bp3-control-indicator" />
|
||||
</label>
|
||||
<Tooltip
|
||||
@@ -143,13 +147,13 @@ class Category extends React.Component {
|
||||
usePortal
|
||||
modifiers={{
|
||||
preventOverflow: { enabled: false },
|
||||
hide: { enabled: false }
|
||||
hide: { enabled: false },
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block"
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
{truncatedString || metadataField}
|
||||
@@ -169,11 +173,11 @@ class Category extends React.Component {
|
||||
const {
|
||||
metadataField,
|
||||
categoricalSelection,
|
||||
colorAccessor,
|
||||
isUserAnno,
|
||||
isColorAccessor,
|
||||
annotations,
|
||||
isExpanded,
|
||||
onExpansionChange
|
||||
onExpansionChange,
|
||||
schema,
|
||||
} = this.props;
|
||||
|
||||
const isStillLoading = !(categoricalSelection?.[metadataField] ?? false);
|
||||
@@ -181,6 +185,8 @@ class Category extends React.Component {
|
||||
return this.renderIsStillLoading();
|
||||
}
|
||||
|
||||
const isUserAnno =
|
||||
schema?.annotations?.obsByName[metadataField]?.writable ?? false;
|
||||
const isTruncated = _.get(
|
||||
categoricalSelection,
|
||||
[metadataField, "isTruncated"],
|
||||
@@ -192,6 +198,20 @@ class Category extends React.Component {
|
||||
globals.categoryDisplayStringMaxLength
|
||||
);
|
||||
|
||||
if (
|
||||
!isUserAnno &&
|
||||
schema?.annotations?.obsByName[metadataField]?.categories?.length === 1
|
||||
) {
|
||||
return (
|
||||
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
||||
<span style={{ fontWeight: 700 }}>
|
||||
{truncatedString ? truncatedString : metadataField}
|
||||
</span>
|
||||
: {schema.annotations.obsByName[metadataField].categories[0]}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CategoryFlipperLayout
|
||||
metadataField={metadataField}
|
||||
@@ -202,7 +222,7 @@ class Category extends React.Component {
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start"
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label className="bp3-control bp3-checkbox">
|
||||
@@ -210,7 +230,7 @@ class Category extends React.Component {
|
||||
data-testclass="category-select"
|
||||
data-testid={`${metadataField}:category-select`}
|
||||
onChange={this.handleToggleAllClick.bind(this)}
|
||||
ref={el => {
|
||||
ref={(el) => {
|
||||
this.checkbox = el;
|
||||
return el;
|
||||
}}
|
||||
@@ -227,14 +247,14 @@ class Category extends React.Component {
|
||||
usePortal
|
||||
modifiers={{
|
||||
preventOverflow: { enabled: false },
|
||||
hide: { enabled: false }
|
||||
hide: { enabled: false },
|
||||
}}
|
||||
>
|
||||
<span
|
||||
data-testid={`${metadataField}:category-expand`}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block"
|
||||
display: "inline-block",
|
||||
}}
|
||||
onClick={() => {
|
||||
const editingCategory =
|
||||
@@ -288,8 +308,8 @@ class Category extends React.Component {
|
||||
data-testclass="colorby"
|
||||
data-testid={`colorby-${metadataField}`}
|
||||
onClick={this.handleColorChange}
|
||||
active={colorAccessor === metadataField}
|
||||
intent={colorAccessor === metadataField ? "primary" : "none"}
|
||||
active={isColorAccessor}
|
||||
intent={isColorAccessor ? "primary" : "none"}
|
||||
disabled={isTruncated}
|
||||
icon="tint"
|
||||
/>
|
||||
+25
-77
@@ -119,57 +119,6 @@ class Categories extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
maybeRenderSingleLabel = (allCategoryNames, schema) => {
|
||||
return allCategoryNames.map((catName) =>
|
||||
!schema.annotations.obsByName[catName].writable &&
|
||||
schema.annotations.obsByName[catName].categories.length === 1 ? (
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<span style={{ fontWeight: 700 }}>{catName}</span>:{" "}
|
||||
{schema.annotations.obsByName[catName].categories[0]}
|
||||
</div>
|
||||
) : null
|
||||
);
|
||||
};
|
||||
maybeRenderReadOnly = (
|
||||
allCategoryNames,
|
||||
schema,
|
||||
expandedCats,
|
||||
createAnnoModeActive
|
||||
) => {
|
||||
return allCategoryNames.map((catName) =>
|
||||
!schema.annotations.obsByName[catName].writable &&
|
||||
schema.annotations.obsByName[catName].categories.length > 1 ? (
|
||||
<Category
|
||||
key={catName}
|
||||
metadataField={catName}
|
||||
onExpansionChange={this.onExpansionChange}
|
||||
isExpanded={expandedCats.has(catName)}
|
||||
createAnnoModeActive={createAnnoModeActive}
|
||||
isUserAnno={false}
|
||||
/>
|
||||
) : null
|
||||
);
|
||||
};
|
||||
maybeRenderAnnotations = (
|
||||
allCategoryNames,
|
||||
schema,
|
||||
expandedCats,
|
||||
createAnnoModeActive
|
||||
) => {
|
||||
return allCategoryNames.map((catName) =>
|
||||
schema.annotations.obsByName[catName].writable ? (
|
||||
<Category
|
||||
key={catName}
|
||||
metadataField={catName}
|
||||
onExpansionChange={this.onExpansionChange}
|
||||
isExpanded={expandedCats.has(catName)}
|
||||
createAnnoModeActive={createAnnoModeActive}
|
||||
isUserAnno
|
||||
/>
|
||||
) : null
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
createAnnoModeActive,
|
||||
@@ -227,32 +176,31 @@ class Categories extends React.Component {
|
||||
}
|
||||
/>
|
||||
|
||||
<div style={{ marginLeft: 0 }}>
|
||||
{
|
||||
this.maybeRenderSingleLabel(
|
||||
allCategoryNames,
|
||||
schema,
|
||||
expandedCats,
|
||||
createAnnoModeActive
|
||||
) /* Categories with only one label */
|
||||
}
|
||||
</div>
|
||||
{
|
||||
this.maybeRenderReadOnly(
|
||||
allCategoryNames,
|
||||
schema,
|
||||
expandedCats,
|
||||
createAnnoModeActive
|
||||
) /* Read only categorical fields, ostensibly 'normal' mode */
|
||||
}
|
||||
{
|
||||
this.maybeRenderAnnotations(
|
||||
allCategoryNames,
|
||||
schema,
|
||||
expandedCats,
|
||||
createAnnoModeActive
|
||||
) /* Writable fields, ie., anno */
|
||||
}
|
||||
{/* READ ONLY CATEGORICAL FIELDS */}
|
||||
{/* this is duplicative but flat, could be abstracted */}
|
||||
{allCategoryNames.map((catName) =>
|
||||
!schema.annotations.obsByName[catName].writable ? (
|
||||
<Category
|
||||
key={catName}
|
||||
metadataField={catName}
|
||||
onExpansionChange={this.onExpansionChange}
|
||||
isExpanded={expandedCats.has(catName)}
|
||||
createAnnoModeActive={createAnnoModeActive}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
{/* WRITEABLE FIELDS */}
|
||||
{allCategoryNames.map((catName) =>
|
||||
schema.annotations.obsByName[catName].writable ? (
|
||||
<Category
|
||||
key={catName}
|
||||
metadataField={catName}
|
||||
onExpansionChange={this.onExpansionChange}
|
||||
isExpanded={expandedCats.has(catName)}
|
||||
createAnnoModeActive={createAnnoModeActive}
|
||||
/>
|
||||
) : null
|
||||
)}
|
||||
|
||||
{writableCategoriesEnabled ? (
|
||||
<div>
|
||||
+40
-40
@@ -8,16 +8,16 @@ import {
|
||||
Popover,
|
||||
Position,
|
||||
PopoverInteractionKind,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
} from "@blueprintjs/core";
|
||||
import Occupancy from "./occupancy";
|
||||
import * as globals from "../../globals";
|
||||
import styles from "./categorical.css";
|
||||
import AnnoDialog from "./annoDialog";
|
||||
import LabelInput from "./labelInput";
|
||||
import * as globals from "../../../globals";
|
||||
import styles from "../categorical.css";
|
||||
import AnnoDialog from "../annoDialog";
|
||||
import LabelInput from "../labelInput";
|
||||
|
||||
import { AnnotationsHelpers } from "../../util/stateManager";
|
||||
import { labelPrompt, isLabelErroneous } from "./labelUtil";
|
||||
import { AnnotationsHelpers } from "../../../util/stateManager";
|
||||
import { labelPrompt, isLabelErroneous } from "../labelUtil";
|
||||
|
||||
/* this is defined outside of the class so we can use it in connect() */
|
||||
function _currentLabel(ownProps, categoricalSelection) {
|
||||
@@ -29,7 +29,7 @@ function _currentLabel(ownProps, categoricalSelection) {
|
||||
|
||||
@connect((state, ownProps) => {
|
||||
const { pointDilation, categoricalSelection } = state;
|
||||
const { metadataField, categoryField } = ownProps;
|
||||
const { metadataField } = ownProps;
|
||||
const isDilated =
|
||||
pointDilation.metadataField === metadataField &&
|
||||
pointDilation.categoryField ===
|
||||
@@ -43,14 +43,14 @@ function _currentLabel(ownProps, categoricalSelection) {
|
||||
world: state.world,
|
||||
crossfilter: state.crossfilter,
|
||||
ontology: state.ontology,
|
||||
isDilated
|
||||
isDilated,
|
||||
};
|
||||
})
|
||||
class CategoryValue extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
editedLabelText: this.currentLabel()
|
||||
editedLabelText: this.currentLabel(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class CategoryValue extends React.Component {
|
||||
prevProps.categoryIndex !== categoryIndex
|
||||
) {
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabel()
|
||||
editedLabelText: this.currentLabel(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class CategoryValue extends React.Component {
|
||||
dispatch({
|
||||
type: "annotation: delete label",
|
||||
metadataField,
|
||||
label
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -85,11 +85,11 @@ class CategoryValue extends React.Component {
|
||||
type: "annotation: label current cell selection",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
handleEditValue = e => {
|
||||
handleEditValue = (e) => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const { editedLabelText } = this.state;
|
||||
const label = this.getLabel();
|
||||
@@ -99,12 +99,12 @@ class CategoryValue extends React.Component {
|
||||
editedLabel: editedLabelText,
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label
|
||||
label,
|
||||
});
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
handleCreateArbitraryLabel = txt => {
|
||||
handleCreateArbitraryLabel = (txt) => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const label = this.getLabel();
|
||||
this.cancelEditMode();
|
||||
@@ -113,17 +113,17 @@ class CategoryValue extends React.Component {
|
||||
metadataField,
|
||||
editedLabel: txt,
|
||||
categoryIndex,
|
||||
label
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
labelNameError = name => {
|
||||
labelNameError = (name) => {
|
||||
const { metadataField, ontology, schema } = this.props;
|
||||
if (name === this.currentLabel()) return false;
|
||||
return isLabelErroneous(name, metadataField, ontology, schema);
|
||||
};
|
||||
|
||||
instruction = label => {
|
||||
instruction = (label) => {
|
||||
return labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
};
|
||||
|
||||
@@ -132,19 +132,19 @@ class CategoryValue extends React.Component {
|
||||
dispatch({
|
||||
type: "annotation: activate edit label mode",
|
||||
metadataField,
|
||||
categoryIndex
|
||||
categoryIndex,
|
||||
});
|
||||
};
|
||||
|
||||
cancelEditMode = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabel()
|
||||
editedLabelText: this.currentLabel(),
|
||||
});
|
||||
dispatch({
|
||||
type: "annotation: cancel edit label mode",
|
||||
metadataField,
|
||||
categoryIndex
|
||||
categoryIndex,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -153,7 +153,7 @@ class CategoryValue extends React.Component {
|
||||
dispatch({
|
||||
type: "categorical metadata filter deselect",
|
||||
metadataField,
|
||||
categoryIndex
|
||||
categoryIndex,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -203,7 +203,7 @@ class CategoryValue extends React.Component {
|
||||
dispatch({
|
||||
type: "categorical metadata filter select",
|
||||
metadataField,
|
||||
categoryIndex
|
||||
categoryIndex,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -212,7 +212,7 @@ class CategoryValue extends React.Component {
|
||||
dispatch({
|
||||
type: "category value mouse hover start",
|
||||
metadataField,
|
||||
categoryIndex
|
||||
categoryIndex,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -221,15 +221,15 @@ class CategoryValue extends React.Component {
|
||||
dispatch({
|
||||
type: "category value mouse hover end",
|
||||
metadataField,
|
||||
categoryIndex
|
||||
categoryIndex,
|
||||
});
|
||||
};
|
||||
|
||||
handleTextChange = text => {
|
||||
handleTextChange = (text) => {
|
||||
this.setState({ editedLabelText: text });
|
||||
};
|
||||
|
||||
handleChoice = e => {
|
||||
handleChoice = (e) => {
|
||||
/* Blueprint Suggest format */
|
||||
this.setState({ editedLabelText: e.target });
|
||||
};
|
||||
@@ -290,7 +290,7 @@ class CategoryValue extends React.Component {
|
||||
// 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,
|
||||
isDilated
|
||||
isDilated,
|
||||
} = this.props;
|
||||
const ontologyEnabled = ontology?.enabled ?? false;
|
||||
|
||||
@@ -359,7 +359,7 @@ class CategoryValue extends React.Component {
|
||||
alignItems: "baseline",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "2px",
|
||||
borderRadius: "2px"
|
||||
borderRadius: "2px",
|
||||
}}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseExit}
|
||||
@@ -371,7 +371,7 @@ class CategoryValue extends React.Component {
|
||||
userSelect: "none",
|
||||
width: globals.leftSidebarWidth - 145,
|
||||
display: "flex",
|
||||
justifyContent: "space-between"
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "baseline" }}>
|
||||
@@ -397,7 +397,7 @@ class CategoryValue extends React.Component {
|
||||
usePortal
|
||||
modifiers={{
|
||||
preventOverflow: { enabled: false },
|
||||
hide: { enabled: false }
|
||||
hide: { enabled: false },
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -417,7 +417,7 @@ class CategoryValue extends React.Component {
|
||||
lineHeight: "1.1em",
|
||||
height: "1.1em",
|
||||
wordBreak: "break-all",
|
||||
verticalAlign: "middle"
|
||||
verticalAlign: "middle",
|
||||
}}
|
||||
>
|
||||
{truncatedString || displayString}
|
||||
@@ -428,10 +428,10 @@ class CategoryValue extends React.Component {
|
||||
<AnnoDialog
|
||||
isActive={editModeActive}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-label-name-dialog`
|
||||
"data-testid": `${metadataField}:edit-label-name-dialog`,
|
||||
}}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:${displayString}:submit-label-edit`
|
||||
"data-testid": `${metadataField}:${displayString}:submit-label-edit`,
|
||||
}}
|
||||
title="Edit label"
|
||||
instruction={this.instruction(editedLabelText)}
|
||||
@@ -452,7 +452,7 @@ class CategoryValue extends React.Component {
|
||||
"data-testid": `${metadataField}:${displayString}:edit-label-name`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true
|
||||
autoFocus: true,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
@@ -483,7 +483,7 @@ class CategoryValue extends React.Component {
|
||||
fontStyle:
|
||||
displayString === globals.unassignedCategoryLabel
|
||||
? "italic"
|
||||
: "auto"
|
||||
: "auto",
|
||||
}}
|
||||
>
|
||||
{count}
|
||||
@@ -498,7 +498,7 @@ class CategoryValue extends React.Component {
|
||||
backgroundColor:
|
||||
isColorBy && categories
|
||||
? colorScale(categories.indexOf(value))
|
||||
: "inherit"
|
||||
: "inherit",
|
||||
}}
|
||||
/>
|
||||
{isUserAnno ? (
|
||||
@@ -526,7 +526,7 @@ class CategoryValue extends React.Component {
|
||||
displayString ===
|
||||
globals.unassignedCategoryLabel
|
||||
? "italic"
|
||||
: "auto"
|
||||
: "auto",
|
||||
}}
|
||||
>
|
||||
{` ${displayString}`}
|
||||
@@ -566,7 +566,7 @@ class CategoryValue extends React.Component {
|
||||
marginLeft: 0,
|
||||
position: "relative",
|
||||
top: -1,
|
||||
minHeight: 16
|
||||
minHeight: 16,
|
||||
}}
|
||||
data-testclass="seeActions"
|
||||
data-testid={`${metadataField}:${displayString}:see-actions`}
|
||||
@@ -1,22 +1,22 @@
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import Categorical from "../categorical/categorical";
|
||||
import Categorical from "../categorical";
|
||||
import * as globals from "../../globals";
|
||||
import DynamicScatterplot from "../scatterplot/scatterplot";
|
||||
import TopLeftLogoAndTitle from "./topLeftLogoAndTitle";
|
||||
|
||||
@connect(state => ({
|
||||
@connect((state) => ({
|
||||
responsive: state.responsive,
|
||||
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
|
||||
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
|
||||
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
|
||||
}))
|
||||
class LeftSideBar extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
responsive,
|
||||
scatterplotXXaccessor,
|
||||
scatterplotYYaccessor
|
||||
scatterplotYYaccessor,
|
||||
} = this.props;
|
||||
|
||||
/*
|
||||
@@ -31,7 +31,7 @@ class LeftSideBar extends React.Component {
|
||||
position: "fixed",
|
||||
backgroundColor: "white",
|
||||
/* x y blur spread color */
|
||||
borderRight: `1px solid ${globals.lightGrey}`
|
||||
borderRight: `1px solid ${globals.lightGrey}`,
|
||||
}}
|
||||
>
|
||||
<TopLeftLogoAndTitle />
|
||||
@@ -41,7 +41,7 @@ class LeftSideBar extends React.Component {
|
||||
marginTop: logoRelatedPadding,
|
||||
width: globals.leftSidebarWidth,
|
||||
overflowY: "auto",
|
||||
overflowX: "hidden"
|
||||
overflowX: "hidden",
|
||||
}}
|
||||
>
|
||||
<Categorical />
|
||||
|
||||
Reference in New Issue
Block a user