mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-04 22:18:12 +08:00
refactor categorical controls state (#1549)
* refactor categorical controls state * lint * fix race condition in tests * fix typo * add missing update on subset * remove obsolete code * update jest and puppeteer major version; update all minors * update when label changes * remove lint from tests; increase timeouts in e2e tests * changes in response to PR review * lint * more PR comment changes * more PR comment fixes * lint * more PR comment resolutions
This commit is contained in:
@@ -21,11 +21,11 @@ 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) {
|
||||
const { metadataField, categoryIndex } = ownProps;
|
||||
return String(
|
||||
categoricalSelection[metadataField].categoryValues[categoryIndex]
|
||||
).valueOf();
|
||||
function _currentLabelAsString(ownProps) {
|
||||
const { categorySummary, categoryIndex } = ownProps;
|
||||
// when called as a function, the String() constructor performs type conversion,
|
||||
// and returns a primtive string.
|
||||
return String(categorySummary.categoryValues[categoryIndex]);
|
||||
}
|
||||
|
||||
@connect((state, ownProps) => {
|
||||
@@ -33,8 +33,7 @@ function _currentLabel(ownProps, categoricalSelection) {
|
||||
const { metadataField } = ownProps;
|
||||
const isDilated =
|
||||
pointDilation.metadataField === metadataField &&
|
||||
pointDilation.categoryField ===
|
||||
_currentLabel(ownProps, categoricalSelection);
|
||||
pointDilation.categoryField === _currentLabelAsString(ownProps);
|
||||
return {
|
||||
categoricalSelection,
|
||||
annotations: state.annotations,
|
||||
@@ -51,28 +50,39 @@ class CategoryValue extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
editedLabelText: this.currentLabel(),
|
||||
editedLabelText: this.currentLabelAsString(),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { categoricalSelection, metadataField, categoryIndex } = this.props;
|
||||
const {
|
||||
categoricalSelection,
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
categorySummary,
|
||||
} = this.props;
|
||||
if (
|
||||
prevProps.categoricalSelection !== categoricalSelection ||
|
||||
prevProps.metadataField !== metadataField ||
|
||||
prevProps.categoryIndex !== categoryIndex
|
||||
prevProps.categoryIndex !== categoryIndex ||
|
||||
prevProps.categorySummary !== categorySummary
|
||||
) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state --- adequately checked to prevent looping
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabel(),
|
||||
editedLabelText: this.currentLabelAsString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
const { categoryIndex, categorySummary } = this.props;
|
||||
const label = categorySummary.categoryValues[categoryIndex];
|
||||
return label;
|
||||
}
|
||||
|
||||
handleDeleteValue = () => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const label = this.getLabel();
|
||||
|
||||
dispatch({
|
||||
type: "annotation: delete label",
|
||||
metadataField,
|
||||
@@ -121,7 +131,7 @@ class CategoryValue extends React.Component {
|
||||
|
||||
labelNameError = (name) => {
|
||||
const { metadataField, ontology, schema } = this.props;
|
||||
if (name === this.currentLabel()) return false;
|
||||
if (name === this.currentLabelAsString()) return false;
|
||||
return isLabelErroneous(name, metadataField, ontology, schema);
|
||||
};
|
||||
|
||||
@@ -131,31 +141,44 @@ class CategoryValue extends React.Component {
|
||||
|
||||
activateEditLabelMode = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const label = this.getLabel();
|
||||
dispatch({
|
||||
type: "annotation: activate edit label mode",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
cancelEditMode = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const label = this.getLabel();
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabel(),
|
||||
editedLabelText: this.currentLabelAsString(),
|
||||
});
|
||||
dispatch({
|
||||
type: "annotation: cancel edit label mode",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
toggleOff = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const {
|
||||
dispatch,
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
categorySummary,
|
||||
} = this.props;
|
||||
const labels = categorySummary.categoryValues;
|
||||
const label = labels[categoryIndex];
|
||||
dispatch({
|
||||
type: "categorical metadata filter deselect",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label,
|
||||
labels,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -170,16 +193,24 @@ class CategoryValue extends React.Component {
|
||||
If and only if true, update the component
|
||||
*/
|
||||
const { props, state } = this;
|
||||
const { metadataField, categoryIndex, categoricalSelection } = props;
|
||||
const { categoricalSelection: newCategoricalSelection } = nextProps;
|
||||
const {
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
categoricalSelection,
|
||||
categorySummary,
|
||||
} = props;
|
||||
const {
|
||||
categoryIndex: newCategoryIndex,
|
||||
categoricalSelection: newCategoricalSelection,
|
||||
categorySummary: newCategorySummary,
|
||||
} = nextProps;
|
||||
|
||||
const label = categorySummary.categoryValues[categoryIndex];
|
||||
const newLabel = newCategorySummary.categoryValues[newCategoryIndex];
|
||||
const labelChanged = label !== newLabel;
|
||||
const valueSelectionChange =
|
||||
categoricalSelection[metadataField].categoryValueSelected[
|
||||
categoryIndex
|
||||
] !==
|
||||
newCategoricalSelection[metadataField].categoryValueSelected[
|
||||
categoryIndex
|
||||
];
|
||||
categoricalSelection[metadataField].get(label) !==
|
||||
newCategoricalSelection[metadataField].get(newLabel);
|
||||
|
||||
const worldChange = props.world !== nextProps.world;
|
||||
const colorAccessorChange = props.colorAccessor !== nextProps.colorAccessor;
|
||||
@@ -189,41 +220,60 @@ class CategoryValue extends React.Component {
|
||||
const editingLabel = state.editedLabelText !== nextState.editedLabelText;
|
||||
const dilationChange = props.isDilated !== nextProps.isDilated;
|
||||
|
||||
const count = categorySummary.categoryValueCounts[categoryIndex];
|
||||
const newCount = newCategorySummary.categoryValueCounts[newCategoryIndex];
|
||||
const countChanged = count !== newCount;
|
||||
|
||||
return (
|
||||
labelChanged ||
|
||||
valueSelectionChange ||
|
||||
worldChange ||
|
||||
colorAccessorChange ||
|
||||
annotationsChange ||
|
||||
crossfilterChange ||
|
||||
editingLabel ||
|
||||
dilationChange
|
||||
dilationChange ||
|
||||
countChanged
|
||||
);
|
||||
};
|
||||
|
||||
toggleOn = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const {
|
||||
dispatch,
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
categorySummary,
|
||||
} = this.props;
|
||||
const labels = categorySummary.categoryValues;
|
||||
const label = labels[categoryIndex];
|
||||
dispatch({
|
||||
type: "categorical metadata filter select",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label,
|
||||
labels,
|
||||
});
|
||||
};
|
||||
|
||||
handleMouseEnter = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const label = this.getLabel();
|
||||
dispatch({
|
||||
type: "category value mouse hover start",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
handleMouseExit = () => {
|
||||
const { dispatch, metadataField, categoryIndex } = this.props;
|
||||
const label = this.getLabel();
|
||||
dispatch({
|
||||
type: "category value mouse hover end",
|
||||
metadataField,
|
||||
categoryIndex,
|
||||
label,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -236,17 +286,8 @@ class CategoryValue extends React.Component {
|
||||
this.setState({ editedLabelText: e.target });
|
||||
};
|
||||
|
||||
getLabel = () => {
|
||||
const { metadataField, categoryIndex, categoricalSelection } = this.props;
|
||||
const category = categoricalSelection[metadataField];
|
||||
const label = category.categoryValues[categoryIndex];
|
||||
|
||||
return label;
|
||||
};
|
||||
|
||||
currentLabel() {
|
||||
const { categoricalSelection } = this.props;
|
||||
return _currentLabel(this.props, categoricalSelection);
|
||||
currentLabelAsString() {
|
||||
return _currentLabelAsString(this.props);
|
||||
}
|
||||
|
||||
isAddCurrentSelectionDisabled(category, value) {
|
||||
@@ -294,6 +335,7 @@ class CategoryValue extends React.Component {
|
||||
flippedProps,
|
||||
isDilated,
|
||||
world,
|
||||
categorySummary,
|
||||
} = this.props;
|
||||
const ontologyEnabled = ontology?.enabled ?? false;
|
||||
|
||||
@@ -302,10 +344,10 @@ class CategoryValue extends React.Component {
|
||||
if (!categoricalSelection) return null;
|
||||
|
||||
const category = categoricalSelection[metadataField];
|
||||
const selected = category.categoryValueSelected[categoryIndex];
|
||||
const count = category.categoryValueCounts[categoryIndex];
|
||||
const value = category.categoryValues[categoryIndex];
|
||||
const displayString = this.currentLabel();
|
||||
const selected = category.get(this.getLabel()) ?? true;
|
||||
const count = categorySummary.categoryValueCounts[categoryIndex];
|
||||
const value = categorySummary.categoryValues[categoryIndex];
|
||||
const displayString = this.currentLabelAsString();
|
||||
|
||||
/* this is the color scale, so add swatches below */
|
||||
const isColorBy = metadataField === colorAccessor;
|
||||
|
||||
Reference in New Issue
Block a user