mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-01 03:08:13 +08:00
TS Revert (1) (#2402)
* revert all commits to before Typescript migration * update compat workflow to match latest deps (#2335) * update compat workflow to match latest deps * attempt to debug * attempt to debug * remove debugging code * typo * update deps to match desktop (#2340) * fix: don't run lint with `--fix` on push tests (#2273) * fix: don't run lint with `--fix` on push tests * npx Co-authored-by: maniarathi <mani.arathi@gmail.com> Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com> * rename X_approx_distribution to X_approximate_distribution (#2337) * Correctly handle non-finite numbers in heuristic determination of X distribution (#2342) * handle non-finites explicitly * improve and test edge case handling for distribution estimation * revert debugging changes * code readability * clean up type inferencing (#2332) * unit tests for 64 bit conversion * clean up type handling * type inference tests * more type inference fixes * use schema to determine user intent for data typing * stop using deprecated API * fbs type encoding test * add missing test * add more tests * correctly infer X type for CXG adaptor * lint * fix typo * ts migration * cleanup from PR review * lint * PR review changes * remove unused packages from client (#2359) * remove unused packages from client * add missing peer dep * fix: disable FE auth testing on compatibility tests (#2377) * update: release process (#2277) Co-authored-by: maniarathi <mani.arathi@gmail.com> * fix: remove spaces in param setup (#2380) * delete deploy workflow (#2396) * undo reformatting which now does not pass lint * fix snapshots which changed due to npm dep changes * add missing quoting to snapshot * another snapshot typo fix * TS Revert (2) - replay PR #2347 and #2354 (#2403) * replay edits from PR 2347 * TS Revert (3) - replay edits in PR #2327 (#2404) * replay edits in PR 2327 * TS Revert (4) - replay PR #2355 (#2405) * replay edits in PR 2355 * add additional babel config * reformat with new prettier config Co-authored-by: Severiano Badajoz <sbadajoz@chanzuckerberg.com> Co-authored-by: maniarathi <mani.arathi@gmail.com> Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com>
This commit is contained in:
co-authored by
maniarathi
Madison Dunitz
Severiano Badajoz
parent
295590a7c6
commit
eaae6df5e3
@@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
import { Button, MenuItem } from "@blueprintjs/core";
|
||||
import { Select } from "@blueprintjs/select";
|
||||
|
||||
class DuplicateCategorySelect extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
allCategoryNames,
|
||||
categoryToDuplicate,
|
||||
handleModalDuplicateCategorySelection,
|
||||
} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
Optionally duplicate all labels & cell assignments from existing
|
||||
category into new category:
|
||||
</p>
|
||||
<Select
|
||||
items={
|
||||
allCategoryNames ||
|
||||
[] /* this is a placeholder, could be a subcomponent to avoid this */
|
||||
}
|
||||
filterable={false}
|
||||
itemRenderer={(d, { handleClick }) => (
|
||||
<MenuItem
|
||||
data-testclass="duplicate-category-dropdown-option"
|
||||
onClick={handleClick}
|
||||
key={d}
|
||||
text={d}
|
||||
/>
|
||||
)}
|
||||
noResults={<MenuItem disabled text="No results." />}
|
||||
onItemSelect={(d) => {
|
||||
handleModalDuplicateCategorySelection(d);
|
||||
}}
|
||||
>
|
||||
{/* children become the popover target; render value here */}
|
||||
<Button
|
||||
data-testid="duplicate-category-dropdown"
|
||||
text={categoryToDuplicate || "None (all cells 'unassigned')"}
|
||||
rightIcon="double-caret-vertical"
|
||||
/>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DuplicateCategorySelect;
|
||||
@@ -1,65 +0,0 @@
|
||||
import React from "react";
|
||||
import { Button, MenuItem } from "@blueprintjs/core";
|
||||
import { Select } from "@blueprintjs/select";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
type State = any;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
class DuplicateCategorySelect extends React.PureComponent<{}, State> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'allCategoryNames' does not exist on type... Remove this comment to see the full error message
|
||||
allCategoryNames,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryToDuplicate' does not exist on t... Remove this comment to see the full error message
|
||||
categoryToDuplicate,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'handleModalDuplicateCategorySelection' d... Remove this comment to see the full error message
|
||||
handleModalDuplicateCategorySelection,
|
||||
} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
Optionally duplicate all labels & cell assignments from existing
|
||||
category into new category:
|
||||
</p>
|
||||
<Select
|
||||
items={
|
||||
allCategoryNames ||
|
||||
[] /* this is a placeholder, could be a subcomponent to avoid this */
|
||||
}
|
||||
filterable={false}
|
||||
itemRenderer={(d, { handleClick }) => (
|
||||
<MenuItem
|
||||
data-testclass="duplicate-category-dropdown-option"
|
||||
onClick={handleClick}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'unknown' is not assignable to type 'Key | nu... Remove this comment to see the full error message
|
||||
key={d}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'unknown' is not assignable to type 'ReactNod... Remove this comment to see the full error message
|
||||
text={d}
|
||||
/>
|
||||
)}
|
||||
noResults={<MenuItem disabled text="No results." />}
|
||||
onItemSelect={(d) => {
|
||||
handleModalDuplicateCategorySelection(d);
|
||||
}}
|
||||
>
|
||||
{/* children become the popover target; render value here */}
|
||||
<Button
|
||||
data-testid="duplicate-category-dropdown"
|
||||
text={categoryToDuplicate || "None (all cells 'unassigned')"}
|
||||
rightIcon="double-caret-vertical"
|
||||
/>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DuplicateCategorySelect;
|
||||
@@ -0,0 +1,113 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import AnnoDialog from "../../annoDialog";
|
||||
import LabelInput from "../../labelInput";
|
||||
import { labelPrompt, isLabelErroneous } from "../labelUtil";
|
||||
import actions from "../../../actions";
|
||||
|
||||
@connect((state) => ({
|
||||
annotations: state.annotations,
|
||||
schema: state.annoMatrix?.schema,
|
||||
obsCrossfilter: state.obsCrossfilter,
|
||||
}))
|
||||
class Category extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
newLabelText: "",
|
||||
};
|
||||
}
|
||||
|
||||
disableAddNewLabelMode = (e) => {
|
||||
const { dispatch } = this.props;
|
||||
this.setState({
|
||||
newLabelText: "",
|
||||
});
|
||||
dispatch({
|
||||
type: "annotation: disable add new label mode",
|
||||
});
|
||||
if (e) e.preventDefault();
|
||||
};
|
||||
|
||||
handleAddNewLabelToCategory = (e) => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newLabelText } = this.state;
|
||||
|
||||
this.disableAddNewLabelMode();
|
||||
dispatch(
|
||||
actions.annotationCreateLabelInCategory(
|
||||
metadataField,
|
||||
newLabelText,
|
||||
false
|
||||
)
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
addLabelAndAssignCells = (e) => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newLabelText } = this.state;
|
||||
|
||||
this.disableAddNewLabelMode();
|
||||
dispatch(
|
||||
actions.annotationCreateLabelInCategory(metadataField, newLabelText, true)
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
labelNameError = (name) => {
|
||||
const { metadataField, schema } = this.props;
|
||||
return isLabelErroneous(name, metadataField, schema);
|
||||
};
|
||||
|
||||
instruction = (label) => labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
|
||||
handleChangeOrSelect = (label) => {
|
||||
this.setState({ newLabelText: label });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { newLabelText } = this.state;
|
||||
const { metadataField, annotations, obsCrossfilter } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnnoDialog
|
||||
isActive={
|
||||
annotations.isAddingNewLabel &&
|
||||
annotations.categoryAddingNewLabel === metadataField
|
||||
}
|
||||
inputProps={{ "data-testid": `${metadataField}:create-label-dialog` }}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:submit-label`,
|
||||
}}
|
||||
title="Add new label to category"
|
||||
instruction={this.instruction(newLabelText)}
|
||||
cancelTooltipContent="Close this dialog without adding a label."
|
||||
primaryButtonText="Add label"
|
||||
secondaryButtonText={`Add label & assign ${obsCrossfilter.countSelected()} selected cells`}
|
||||
handleSecondaryButtonSubmit={this.addLabelAndAssignCells}
|
||||
text={newLabelText}
|
||||
validationError={this.labelNameError(newLabelText)}
|
||||
handleSubmit={this.handleAddNewLabelToCategory}
|
||||
handleCancel={this.disableAddNewLabelMode}
|
||||
annoInput={
|
||||
<LabelInput
|
||||
labelSuggestions={null}
|
||||
onChange={this.handleChangeOrSelect}
|
||||
onSelect={this.handleChangeOrSelect}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:new-label-name`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Category;
|
||||
@@ -1,136 +0,0 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import AnnoDialog from "../../annoDialog";
|
||||
import LabelInput from "../../labelInput";
|
||||
import { labelPrompt, isLabelErroneous } from "../labelUtil";
|
||||
import actions from "../../../actions";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
type State = any;
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
annotations: (state as any).annotations,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
schema: (state as any).annoMatrix?.schema,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
obsCrossfilter: (state as any).obsCrossfilter,
|
||||
}))
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
class Category extends React.PureComponent<{}, State> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {
|
||||
newLabelText: "",
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
disableAddNewLabelMode = (e: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch } = this.props;
|
||||
this.setState({
|
||||
newLabelText: "",
|
||||
});
|
||||
dispatch({
|
||||
type: "annotation: disable add new label mode",
|
||||
});
|
||||
if (e) e.preventDefault();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleAddNewLabelToCategory = (e: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newLabelText } = this.state;
|
||||
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
|
||||
this.disableAddNewLabelMode();
|
||||
dispatch(
|
||||
actions.annotationCreateLabelInCategory(
|
||||
metadataField,
|
||||
newLabelText,
|
||||
false
|
||||
)
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
addLabelAndAssignCells = (e: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newLabelText } = this.state;
|
||||
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
|
||||
this.disableAddNewLabelMode();
|
||||
dispatch(
|
||||
actions.annotationCreateLabelInCategory(metadataField, newLabelText, true)
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
labelNameError = (name: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { metadataField, schema } = this.props;
|
||||
return isLabelErroneous(name, metadataField, schema);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
instruction = (label: any) =>
|
||||
labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleChangeOrSelect = (label: any) => {
|
||||
this.setState({ newLabelText: label });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const { newLabelText } = this.state;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { metadataField, annotations, obsCrossfilter } = this.props;
|
||||
return (
|
||||
<>
|
||||
<AnnoDialog
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isActive: any; inputProps: { "data-testid"... Remove this comment to see the full error message
|
||||
isActive={
|
||||
annotations.isAddingNewLabel &&
|
||||
annotations.categoryAddingNewLabel === metadataField
|
||||
}
|
||||
inputProps={{ "data-testid": `${metadataField}:create-label-dialog` }}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:submit-label`,
|
||||
}}
|
||||
title="Add new label to category"
|
||||
instruction={this.instruction(newLabelText)}
|
||||
cancelTooltipContent="Close this dialog without adding a label."
|
||||
primaryButtonText="Add label"
|
||||
secondaryButtonText={`Add label & assign ${obsCrossfilter.countSelected()} selected cells`}
|
||||
handleSecondaryButtonSubmit={this.addLabelAndAssignCells}
|
||||
text={newLabelText}
|
||||
validationError={this.labelNameError(newLabelText)}
|
||||
handleSubmit={this.handleAddNewLabelToCategory}
|
||||
handleCancel={this.disableAddNewLabelMode}
|
||||
annoInput={
|
||||
<LabelInput
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ labelSuggestions: null; onChange: (label: ... Remove this comment to see the full error message
|
||||
labelSuggestions={null}
|
||||
onChange={this.handleChangeOrSelect}
|
||||
onSelect={this.handleChangeOrSelect}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:new-label-name`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Category;
|
||||
@@ -0,0 +1,149 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import AnnoDialog from "../../annoDialog";
|
||||
import LabelInput from "../../labelInput";
|
||||
import { labelPrompt } from "../labelUtil";
|
||||
|
||||
import { AnnotationsHelpers } from "../../../util/stateManager";
|
||||
import actions from "../../../actions";
|
||||
|
||||
@connect((state) => ({
|
||||
annotations: state.annotations,
|
||||
schema: state.annoMatrix?.schema,
|
||||
}))
|
||||
class AnnoDialogEditCategoryName extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
newCategoryText: props.metadataField,
|
||||
};
|
||||
}
|
||||
|
||||
handleChangeOrSelect = (name) => {
|
||||
this.setState({
|
||||
newCategoryText: name,
|
||||
});
|
||||
};
|
||||
|
||||
disableEditCategoryMode = () => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "annotation: disable category edit mode",
|
||||
});
|
||||
this.setState({ newCategoryText: metadataField });
|
||||
};
|
||||
|
||||
handleEditCategory = (e) => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newCategoryText } = this.state;
|
||||
|
||||
/*
|
||||
test for uniqueness against *all* annotation names, not just the subset
|
||||
we render as categorical.
|
||||
*/
|
||||
const { schema } = this.props;
|
||||
const allCategoryNames = schema.annotations.obs.columns.map((c) => c.name);
|
||||
|
||||
if (
|
||||
(allCategoryNames.indexOf(newCategoryText) > -1 &&
|
||||
newCategoryText !== metadataField) ||
|
||||
newCategoryText === ""
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.disableEditCategoryMode();
|
||||
|
||||
if (metadataField !== newCategoryText)
|
||||
dispatch(
|
||||
actions.annotationRenameCategoryAction(metadataField, newCategoryText)
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
editedCategoryNameError = (name) => {
|
||||
const { metadataField } = this.props;
|
||||
|
||||
/* check for syntax errors in category name */
|
||||
const error = AnnotationsHelpers.annotationNameIsErroneous(name);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
/* check for duplicative categories */
|
||||
|
||||
/*
|
||||
test for uniqueness against *all* annotation names, not just the subset
|
||||
we render as categorical.
|
||||
*/
|
||||
const { schema } = this.props;
|
||||
const allCategoryNames = schema.annotations.obs.columns.map((c) => c.name);
|
||||
|
||||
const categoryNameAlreadyExists = allCategoryNames.indexOf(name) > -1;
|
||||
const sameName = name === metadataField;
|
||||
if (categoryNameAlreadyExists && !sameName) {
|
||||
return "duplicate";
|
||||
}
|
||||
|
||||
/* otherwise, no error */
|
||||
return false;
|
||||
};
|
||||
|
||||
instruction = (name) => labelPrompt(
|
||||
this.editedCategoryNameError(name),
|
||||
"New, unique category name",
|
||||
":"
|
||||
);
|
||||
|
||||
allCategoryNames() {
|
||||
const { schema } = this.props;
|
||||
return schema.annotations.obs.columns.map((c) => c.name);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { newCategoryText } = this.state;
|
||||
const { metadataField, annotations } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnnoDialog
|
||||
isActive={
|
||||
annotations.isEditingCategoryName &&
|
||||
annotations.categoryBeingEdited === metadataField
|
||||
}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-category-name-dialog`,
|
||||
}}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:submit-category-edit`,
|
||||
}}
|
||||
title="Edit category name"
|
||||
instruction={this.instruction(newCategoryText)}
|
||||
cancelTooltipContent="Close this dialog without editing this category."
|
||||
primaryButtonText="Edit category name"
|
||||
text={newCategoryText}
|
||||
validationError={this.editedCategoryNameError(newCategoryText)}
|
||||
handleSubmit={this.handleEditCategory}
|
||||
handleCancel={this.disableEditCategoryMode}
|
||||
annoInput={
|
||||
<LabelInput
|
||||
label={newCategoryText}
|
||||
labelSuggestions={null}
|
||||
onChange={this.handleChangeOrSelect}
|
||||
onSelect={this.handleChangeOrSelect}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-category-name-text`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true,
|
||||
}}
|
||||
newLabelMessage="New category"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AnnoDialogEditCategoryName;
|
||||
@@ -1,172 +0,0 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import AnnoDialog from "../../annoDialog";
|
||||
import LabelInput from "../../labelInput";
|
||||
import { labelPrompt } from "../labelUtil";
|
||||
|
||||
import { AnnotationsHelpers } from "../../../util/stateManager";
|
||||
import actions from "../../../actions";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
type State = any;
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
annotations: (state as any).annotations,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
schema: (state as any).annoMatrix?.schema,
|
||||
}))
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
class AnnoDialogEditCategoryName extends React.PureComponent<{}, State> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(props: {}) {
|
||||
super(props);
|
||||
this.state = {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type '{... Remove this comment to see the full error message
|
||||
newCategoryText: props.metadataField,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleChangeOrSelect = (name: any) => {
|
||||
this.setState({
|
||||
newCategoryText: name,
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
disableEditCategoryMode = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "annotation: disable category edit mode",
|
||||
});
|
||||
this.setState({ newCategoryText: metadataField });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleEditCategory = (e: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
const { newCategoryText } = this.state;
|
||||
/*
|
||||
test for uniqueness against *all* annotation names, not just the subset
|
||||
we render as categorical.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
const { schema } = this.props;
|
||||
const allCategoryNames = schema.annotations.obs.columns.map(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(c: any) => c.name
|
||||
);
|
||||
if (
|
||||
(allCategoryNames.indexOf(newCategoryText) > -1 &&
|
||||
newCategoryText !== metadataField) ||
|
||||
newCategoryText === ""
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.disableEditCategoryMode();
|
||||
if (metadataField !== newCategoryText)
|
||||
dispatch(
|
||||
actions.annotationRenameCategoryAction(metadataField, newCategoryText)
|
||||
);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
editedCategoryNameError = (name: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { metadataField } = this.props;
|
||||
/* check for syntax errors in category name */
|
||||
const error = AnnotationsHelpers.annotationNameIsErroneous(name);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
/* check for duplicative categories */
|
||||
/*
|
||||
test for uniqueness against *all* annotation names, not just the subset
|
||||
we render as categorical.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
const { schema } = this.props;
|
||||
const allCategoryNames = schema.annotations.obs.columns.map(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(c: any) => c.name
|
||||
);
|
||||
const categoryNameAlreadyExists = allCategoryNames.indexOf(name) > -1;
|
||||
const sameName = name === metadataField;
|
||||
if (categoryNameAlreadyExists && !sameName) {
|
||||
return "duplicate";
|
||||
}
|
||||
/* otherwise, no error */
|
||||
return false;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
instruction = (name: any) =>
|
||||
labelPrompt(
|
||||
this.editedCategoryNameError(name),
|
||||
"New, unique category name",
|
||||
":"
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
allCategoryNames() {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
const { schema } = this.props;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return schema.annotations.obs.columns.map((c: any) => c.name);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const { newCategoryText } = this.state;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { metadataField, annotations } = this.props;
|
||||
return (
|
||||
<>
|
||||
<AnnoDialog
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isActive: any; inputProps: { "data-testid"... Remove this comment to see the full error message
|
||||
isActive={
|
||||
annotations.isEditingCategoryName &&
|
||||
annotations.categoryBeingEdited === metadataField
|
||||
}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-category-name-dialog`,
|
||||
}}
|
||||
primaryButtonProps={{
|
||||
"data-testid": `${metadataField}:submit-category-edit`,
|
||||
}}
|
||||
title="Edit category name"
|
||||
instruction={this.instruction(newCategoryText)}
|
||||
cancelTooltipContent="Close this dialog without editing this category."
|
||||
primaryButtonText="Edit category name"
|
||||
text={newCategoryText}
|
||||
validationError={this.editedCategoryNameError(newCategoryText)}
|
||||
handleSubmit={this.handleEditCategory}
|
||||
handleCancel={this.disableEditCategoryMode}
|
||||
annoInput={
|
||||
<LabelInput
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ label: any; labelSuggestions: null; onChan... Remove this comment to see the full error message
|
||||
label={newCategoryText}
|
||||
labelSuggestions={null}
|
||||
onChange={this.handleChangeOrSelect}
|
||||
onSelect={this.handleChangeOrSelect}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-category-name-text`,
|
||||
leftIcon: "tag",
|
||||
intent: "none",
|
||||
autoFocus: true,
|
||||
}}
|
||||
newLabelMessage="New category"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AnnoDialogEditCategoryName;
|
||||
+5
-23
@@ -16,25 +16,16 @@ import { IconNames } from "@blueprintjs/icons";
|
||||
import * as globals from "../../../globals";
|
||||
import actions from "../../../actions";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
type State = any;
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
annotations: (state as any).annotations,
|
||||
annotations: state.annotations,
|
||||
}))
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
class AnnoMenuCategory extends React.PureComponent<{}, State> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(props: {}) {
|
||||
class AnnoMenuCategory extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
activateAddNewLabelMode = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "annotation: activate add new label mode",
|
||||
@@ -42,39 +33,30 @@ class AnnoMenuCategory extends React.PureComponent<{}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
activateEditCategoryMode = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
|
||||
dispatch({
|
||||
type: "annotation: activate category edit mode",
|
||||
data: metadataField,
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleDeleteCategory = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch(actions.annotationDeleteCategoryAction(metadataField));
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annotations' does not exist on type 'Rea... Remove this comment to see the full error message
|
||||
annotations,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isUserAnno' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
isUserAnno,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'createText' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
createText,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'editText' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
editText,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'deleteText' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
deleteText,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isUserAnno ? (
|
||||
@@ -0,0 +1,617 @@
|
||||
import React, { useRef, useEffect } from "react";
|
||||
import { connect, shallowEqual } from "react-redux";
|
||||
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
|
||||
import {
|
||||
AnchorButton,
|
||||
Button,
|
||||
Classes,
|
||||
Position,
|
||||
Tooltip,
|
||||
} from "@blueprintjs/core";
|
||||
import { Flipper, Flipped } from "react-flip-toolkit";
|
||||
import Async from "react-async";
|
||||
import memoize from "memoize-one";
|
||||
|
||||
import Value from "../value";
|
||||
import AnnoMenu from "./annoMenuCategory";
|
||||
import AnnoDialogEditCategoryName from "./annoDialogEditCategoryName";
|
||||
import AnnoDialogAddLabel from "./annoDialogAddLabel";
|
||||
import Truncate from "../../util/truncate";
|
||||
import { CategoryCrossfilterContext } from "../categoryContext";
|
||||
|
||||
import * as globals from "../../../globals";
|
||||
import { createCategorySummaryFromDfCol } from "../../../util/stateManager/controlsHelpers";
|
||||
import {
|
||||
createColorTable,
|
||||
createColorQuery,
|
||||
} from "../../../util/stateManager/colorHelpers";
|
||||
import actions from "../../../actions";
|
||||
|
||||
const LABEL_WIDTH = globals.leftSidebarWidth - 100;
|
||||
const ANNO_BUTTON_WIDTH = 50;
|
||||
const LABEL_WIDTH_ANNO = LABEL_WIDTH - ANNO_BUTTON_WIDTH;
|
||||
|
||||
@connect((state, ownProps) => {
|
||||
const schema = state.annoMatrix?.schema;
|
||||
const { metadataField } = ownProps;
|
||||
const isUserAnno = schema?.annotations?.obsByName[metadataField]?.writable;
|
||||
const categoricalSelection = state.categoricalSelection?.[metadataField];
|
||||
return {
|
||||
colors: state.colors,
|
||||
categoricalSelection,
|
||||
annotations: state.annotations,
|
||||
annoMatrix: state.annoMatrix,
|
||||
schema,
|
||||
crossfilter: state.obsCrossfilter,
|
||||
isUserAnno,
|
||||
genesets: state.genesets.genesets,
|
||||
};
|
||||
})
|
||||
class Category extends React.PureComponent {
|
||||
static getSelectionState(
|
||||
categoricalSelection,
|
||||
metadataField,
|
||||
categorySummary
|
||||
) {
|
||||
// total number of categories in this dimension
|
||||
const totalCatCount = categorySummary.numCategoryValues;
|
||||
// number of selected options in this category
|
||||
const selectedCatCount = categorySummary.categoryValues.reduce(
|
||||
(res, label) => (categoricalSelection.get(label) ?? true ? res + 1 : res),
|
||||
0
|
||||
);
|
||||
return selectedCatCount === totalCatCount
|
||||
? "all"
|
||||
: selectedCatCount === 0
|
||||
? "none"
|
||||
: "some";
|
||||
}
|
||||
|
||||
static watchAsync(props, prevProps) {
|
||||
return !shallowEqual(props.watchProps, prevProps.watchProps);
|
||||
}
|
||||
|
||||
createCategorySummaryFromDfCol = memoize(createCategorySummaryFromDfCol);
|
||||
|
||||
getSelectionState(categorySummary) {
|
||||
const { categoricalSelection, metadataField } = this.props;
|
||||
return Category.getSelectionState(
|
||||
categoricalSelection,
|
||||
metadataField,
|
||||
categorySummary
|
||||
);
|
||||
}
|
||||
|
||||
handleColorChange = () => {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "color by categorical metadata",
|
||||
colorAccessor: metadataField,
|
||||
});
|
||||
};
|
||||
|
||||
handleCategoryClick = () => {
|
||||
const { annotations, metadataField, onExpansionChange } = this.props;
|
||||
const editingCategory =
|
||||
annotations.isEditingCategoryName &&
|
||||
annotations.categoryBeingEdited === metadataField;
|
||||
if (!editingCategory) {
|
||||
onExpansionChange(metadataField);
|
||||
}
|
||||
};
|
||||
|
||||
handleCategoryKeyPress = (e) => {
|
||||
if (e.key === "Enter") {
|
||||
this.handleCategoryClick();
|
||||
}
|
||||
};
|
||||
|
||||
handleToggleAllClick = (categorySummary) => {
|
||||
const isChecked = this.getSelectionState(categorySummary);
|
||||
if (isChecked === "all") {
|
||||
this.toggleNone(categorySummary);
|
||||
} else {
|
||||
this.toggleAll(categorySummary);
|
||||
}
|
||||
};
|
||||
|
||||
fetchAsyncProps = async (props) => {
|
||||
const { annoMatrix, metadataField, colors } = props.watchProps;
|
||||
const { crossfilter } = this.props;
|
||||
|
||||
const [categoryData, categorySummary, colorData] = await this.fetchData(
|
||||
annoMatrix,
|
||||
metadataField,
|
||||
colors
|
||||
);
|
||||
|
||||
return {
|
||||
categoryData,
|
||||
categorySummary,
|
||||
colorData,
|
||||
crossfilter,
|
||||
...this.updateColorTable(colorData),
|
||||
handleCategoryToggleAllClick: () =>
|
||||
this.handleToggleAllClick(categorySummary),
|
||||
};
|
||||
};
|
||||
|
||||
async fetchData(annoMatrix, metadataField, colors) {
|
||||
/*
|
||||
fetch our data and the color-by data if appropriate, and then build a summary
|
||||
of our category and a color table for the color-by annotation.
|
||||
*/
|
||||
const { schema } = annoMatrix;
|
||||
const { colorAccessor, colorMode } = colors;
|
||||
const { genesets } = this.props;
|
||||
let colorDataPromise = Promise.resolve(null);
|
||||
if (colorAccessor) {
|
||||
const query = createColorQuery(
|
||||
colorMode,
|
||||
colorAccessor,
|
||||
schema,
|
||||
genesets
|
||||
);
|
||||
if (query) colorDataPromise = annoMatrix.fetch(...query);
|
||||
}
|
||||
const [categoryData, colorData] = await Promise.all([
|
||||
annoMatrix.fetch("obs", metadataField),
|
||||
colorDataPromise,
|
||||
]);
|
||||
|
||||
// our data
|
||||
const column = categoryData.icol(0);
|
||||
const colSchema = schema.annotations.obsByName[metadataField];
|
||||
const categorySummary = this.createCategorySummaryFromDfCol(
|
||||
column,
|
||||
colSchema
|
||||
);
|
||||
return [categoryData, categorySummary, colorData];
|
||||
}
|
||||
|
||||
updateColorTable(colorData) {
|
||||
// color table, which may be null
|
||||
const { schema, colors, metadataField } = this.props;
|
||||
const { colorAccessor, userColors, colorMode } = colors;
|
||||
return {
|
||||
isColorAccessor: colorAccessor === metadataField,
|
||||
colorAccessor,
|
||||
colorMode,
|
||||
colorTable: createColorTable(
|
||||
colorMode,
|
||||
colorAccessor,
|
||||
colorData,
|
||||
schema,
|
||||
userColors
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
toggleNone(categorySummary) {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch(
|
||||
actions.selectCategoricalAllMetadataAction(
|
||||
"categorical metadata filter none of these",
|
||||
metadataField,
|
||||
categorySummary.allCategoryValues,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
toggleAll(categorySummary) {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch(
|
||||
actions.selectCategoricalAllMetadataAction(
|
||||
"categorical metadata filter all of these",
|
||||
metadataField,
|
||||
categorySummary.allCategoryValues,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
metadataField,
|
||||
isExpanded,
|
||||
categoricalSelection,
|
||||
crossfilter,
|
||||
colors,
|
||||
annoMatrix,
|
||||
isUserAnno,
|
||||
} = this.props;
|
||||
|
||||
const checkboxID = `category-select-${metadataField}`;
|
||||
|
||||
return (
|
||||
<CategoryCrossfilterContext.Provider value={crossfilter}>
|
||||
<Async
|
||||
watchFn={Category.watchAsync}
|
||||
promiseFn={this.fetchAsyncProps}
|
||||
watchProps={{
|
||||
metadataField,
|
||||
annoMatrix,
|
||||
categoricalSelection,
|
||||
colors,
|
||||
}}
|
||||
>
|
||||
<Async.Pending initial>
|
||||
<StillLoading
|
||||
metadataField={metadataField}
|
||||
checkboxID={checkboxID}
|
||||
/>
|
||||
</Async.Pending>
|
||||
<Async.Rejected>
|
||||
{(error) => (
|
||||
<ErrorLoading metadataField={metadataField} error={error} />
|
||||
)}
|
||||
</Async.Rejected>
|
||||
<Async.Fulfilled persist>
|
||||
{(asyncProps) => {
|
||||
const {
|
||||
colorAccessor,
|
||||
colorTable,
|
||||
colorData,
|
||||
categoryData,
|
||||
categorySummary,
|
||||
isColorAccessor,
|
||||
handleCategoryToggleAllClick,
|
||||
} = asyncProps;
|
||||
const selectionState = this.getSelectionState(categorySummary);
|
||||
return (
|
||||
<CategoryRender
|
||||
metadataField={metadataField}
|
||||
checkboxID={checkboxID}
|
||||
isUserAnno={isUserAnno}
|
||||
isExpanded={isExpanded}
|
||||
isColorAccessor={isColorAccessor}
|
||||
selectionState={selectionState}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
onColorChangeClick={this.handleColorChange}
|
||||
onCategoryToggleAllClick={handleCategoryToggleAllClick}
|
||||
onCategoryMenuClick={this.handleCategoryClick}
|
||||
onCategoryMenuKeyPress={this.handleCategoryKeyPress}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Async.Fulfilled>
|
||||
</Async>
|
||||
</CategoryCrossfilterContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Category;
|
||||
|
||||
const StillLoading = ({ metadataField, checkboxID }) =>
|
||||
/*
|
||||
We are still loading this category, so render a "busy" signal.
|
||||
*/
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
maxWidth: globals.maxControlsWidth,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
htmlFor={checkboxID}
|
||||
className={`${Classes.CONTROL} ${Classes.CHECKBOX}`}
|
||||
>
|
||||
<input disabled id={checkboxID} checked type="checkbox" />
|
||||
<span className={Classes.CONTROL_INDICATOR} />
|
||||
</label>
|
||||
<Truncate>
|
||||
<span
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
width: LABEL_WIDTH,
|
||||
}}
|
||||
>
|
||||
{metadataField}
|
||||
</span>
|
||||
</Truncate>
|
||||
</div>
|
||||
<div>
|
||||
<Button minimal loading intent="primary" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
;
|
||||
|
||||
const ErrorLoading = ({ metadataField, error }) => {
|
||||
console.error(error); // log error to console as it is unexpected.
|
||||
return (
|
||||
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
||||
<span
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
width: LABEL_WIDTH,
|
||||
fontStyle: "italic",
|
||||
}}
|
||||
>
|
||||
{`Failure loading ${metadataField}`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CategoryHeader = React.memo(
|
||||
({
|
||||
metadataField,
|
||||
checkboxID,
|
||||
isUserAnno,
|
||||
isColorAccessor,
|
||||
isExpanded,
|
||||
selectionState,
|
||||
onColorChangeClick,
|
||||
onCategoryMenuClick,
|
||||
onCategoryMenuKeyPress,
|
||||
onCategoryToggleAllClick,
|
||||
}) => {
|
||||
/*
|
||||
Render category name and controls (eg, color-by button).
|
||||
*/
|
||||
const checkboxRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
checkboxRef.current.indeterminate = selectionState === "some";
|
||||
}, [checkboxRef.current, selectionState]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className={`${Classes.CONTROL} ${Classes.CHECKBOX}`}
|
||||
htmlFor={checkboxID}
|
||||
>
|
||||
<input
|
||||
id={checkboxID}
|
||||
data-testclass="category-select"
|
||||
data-testid={`${metadataField}:category-select`}
|
||||
onChange={onCategoryToggleAllClick}
|
||||
ref={checkboxRef}
|
||||
checked={selectionState === "all"}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span className={Classes.CONTROL_INDICATOR} />
|
||||
</label>
|
||||
<span
|
||||
role="menuitem"
|
||||
tabIndex="0"
|
||||
data-testclass="category-expand"
|
||||
data-testid={`${metadataField}:category-expand`}
|
||||
onKeyPress={onCategoryMenuKeyPress}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={onCategoryMenuClick}
|
||||
>
|
||||
<Truncate>
|
||||
<span
|
||||
style={{
|
||||
maxWidth: isUserAnno ? LABEL_WIDTH_ANNO : LABEL_WIDTH,
|
||||
}}
|
||||
data-testid={`${metadataField}:category-label`}
|
||||
tabIndex="-1"
|
||||
>
|
||||
{metadataField}
|
||||
</span>
|
||||
</Truncate>
|
||||
{isExpanded ? (
|
||||
<FaChevronDown
|
||||
data-testclass="category-expand-is-expanded"
|
||||
style={{ fontSize: 10, marginLeft: 5 }}
|
||||
/>
|
||||
) : (
|
||||
<FaChevronRight
|
||||
data-testclass="category-expand-is-not-expanded"
|
||||
style={{ fontSize: 10, marginLeft: 5 }}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{<AnnoDialogEditCategoryName metadataField={metadataField} />}
|
||||
{<AnnoDialogAddLabel metadataField={metadataField} />}
|
||||
<div>
|
||||
<AnnoMenu
|
||||
metadataField={metadataField}
|
||||
isUserAnno={isUserAnno}
|
||||
createText="Add a new label to this category"
|
||||
editText="Edit this category's name"
|
||||
deleteText="Delete this category, all associated labels, and remove all cell assignments"
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
content="Use as color scale"
|
||||
position={Position.LEFT}
|
||||
usePortal
|
||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||
modifiers={{
|
||||
preventOverflow: { enabled: false },
|
||||
hide: { enabled: false },
|
||||
}}
|
||||
>
|
||||
<AnchorButton
|
||||
data-testclass="colorby"
|
||||
data-testid={`colorby-${metadataField}`}
|
||||
onClick={onColorChangeClick}
|
||||
active={isColorAccessor}
|
||||
intent={isColorAccessor ? "primary" : "none"}
|
||||
icon="tint"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const CategoryRender = React.memo(
|
||||
({
|
||||
metadataField,
|
||||
checkboxID,
|
||||
isUserAnno,
|
||||
isColorAccessor,
|
||||
isExpanded,
|
||||
selectionState,
|
||||
categoryData,
|
||||
categorySummary,
|
||||
colorAccessor,
|
||||
colorData,
|
||||
colorTable,
|
||||
onColorChangeClick,
|
||||
onCategoryMenuClick,
|
||||
onCategoryMenuKeyPress,
|
||||
onCategoryToggleAllClick,
|
||||
}) => {
|
||||
/*
|
||||
Render the core of the category, including checkboxes, controls, etc.
|
||||
*/
|
||||
const { numCategoryValues } = categorySummary;
|
||||
const isSingularValue = !isUserAnno && numCategoryValues === 1;
|
||||
|
||||
if (isSingularValue) {
|
||||
/*
|
||||
Entire category has a single value, special case.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
Otherwise, our normal multi-layout layout
|
||||
*/
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: globals.maxControlsWidth,
|
||||
}}
|
||||
data-testclass="category"
|
||||
data-testid={`category-${metadataField}`}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline",
|
||||
}}
|
||||
>
|
||||
<CategoryHeader
|
||||
metadataField={metadataField}
|
||||
checkboxID={checkboxID}
|
||||
isUserAnno={isUserAnno}
|
||||
isExpanded={isExpanded}
|
||||
isColorAccessor={isColorAccessor}
|
||||
selectionState={selectionState}
|
||||
onColorChangeClick={onColorChangeClick}
|
||||
onCategoryToggleAllClick={onCategoryToggleAllClick}
|
||||
onCategoryMenuClick={onCategoryMenuClick}
|
||||
onCategoryMenuKeyPress={onCategoryMenuKeyPress}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginLeft: 26 }}>
|
||||
{
|
||||
/* values*/
|
||||
isExpanded ? (
|
||||
<CategoryValueList
|
||||
isUserAnno={isUserAnno}
|
||||
metadataField={metadataField}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const CategoryValueList = React.memo(
|
||||
({
|
||||
isUserAnno,
|
||||
metadataField,
|
||||
categoryData,
|
||||
categorySummary,
|
||||
colorAccessor,
|
||||
colorData,
|
||||
colorTable,
|
||||
}) => {
|
||||
const tuples = [...categorySummary.categoryValueIndices];
|
||||
|
||||
/*
|
||||
Render the value list. If this is a user annotation, we use a flipper
|
||||
animation, if read-only, we don't bother and save a few bits of perf.
|
||||
*/
|
||||
if (!isUserAnno) {
|
||||
return (
|
||||
<>
|
||||
{tuples.map(([value, index]) => (
|
||||
<Value
|
||||
key={value}
|
||||
isUserAnno={isUserAnno}
|
||||
metadataField={metadataField}
|
||||
categoryIndex={index}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* User annotation */
|
||||
const flipKey = tuples.map((t) => t[0]).join("");
|
||||
return (
|
||||
<Flipper flipKey={flipKey}>
|
||||
{tuples.map(([value, index]) => (
|
||||
<Flipped key={value} flipId={value}>
|
||||
<Value
|
||||
isUserAnno={isUserAnno}
|
||||
metadataField={metadataField}
|
||||
categoryIndex={index}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
/>
|
||||
</Flipped>
|
||||
))}
|
||||
</Flipper>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -1,724 +0,0 @@
|
||||
import React, { useRef, useEffect } from "react";
|
||||
import { connect, shallowEqual } from "react-redux";
|
||||
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
|
||||
import {
|
||||
AnchorButton,
|
||||
Button,
|
||||
Classes,
|
||||
Position,
|
||||
Tooltip,
|
||||
} from "@blueprintjs/core";
|
||||
import { Flipper, Flipped } from "react-flip-toolkit";
|
||||
import Async from "react-async";
|
||||
import memoize from "memoize-one";
|
||||
|
||||
import Value from "../value";
|
||||
import AnnoMenu from "./annoMenuCategory";
|
||||
import AnnoDialogEditCategoryName from "./annoDialogEditCategoryName";
|
||||
import AnnoDialogAddLabel from "./annoDialogAddLabel";
|
||||
import Truncate from "../../util/truncate";
|
||||
import { CategoryCrossfilterContext } from "../categoryContext";
|
||||
|
||||
import * as globals from "../../../globals";
|
||||
import { createCategorySummaryFromDfCol } from "../../../util/stateManager/controlsHelpers";
|
||||
import {
|
||||
createColorTable,
|
||||
createColorQuery,
|
||||
} from "../../../util/stateManager/colorHelpers";
|
||||
import actions from "../../../actions";
|
||||
import { Dataframe } from "../../../util/dataframe";
|
||||
|
||||
const LABEL_WIDTH = globals.leftSidebarWidth - 100;
|
||||
const ANNO_BUTTON_WIDTH = 50;
|
||||
const LABEL_WIDTH_ANNO = LABEL_WIDTH - ANNO_BUTTON_WIDTH;
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state, ownProps) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const schema = (state as any).annoMatrix?.schema;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type '{... Remove this comment to see the full error message
|
||||
const { metadataField } = ownProps;
|
||||
const isUserAnno = schema?.annotations?.obsByName[metadataField]?.writable;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const categoricalSelection = (state as any).categoricalSelection?.[
|
||||
metadataField
|
||||
];
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
colors: (state as any).colors,
|
||||
categoricalSelection,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
annotations: (state as any).annotations,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
annoMatrix: (state as any).annoMatrix,
|
||||
schema,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
crossfilter: (state as any).obsCrossfilter,
|
||||
isUserAnno,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
genesets: (state as any).genesets.genesets,
|
||||
};
|
||||
})
|
||||
class Category extends React.PureComponent {
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
static getSelectionState(
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
categoricalSelection: any,
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'metadataField' is declared but its value is never... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
metadataField: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
categorySummary: any
|
||||
) {
|
||||
// total number of categories in this dimension
|
||||
const totalCatCount = categorySummary.numCategoryValues;
|
||||
// number of selected options in this category
|
||||
const selectedCatCount = categorySummary.categoryValues.reduce(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(res: any, label: any) =>
|
||||
categoricalSelection.get(label) ?? true ? res + 1 : res,
|
||||
0
|
||||
);
|
||||
return selectedCatCount === totalCatCount
|
||||
? "all"
|
||||
: selectedCatCount === 0
|
||||
? "none"
|
||||
: "some";
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
static watchAsync(props: any, prevProps: any) {
|
||||
return !shallowEqual(props.watchProps, prevProps.watchProps);
|
||||
}
|
||||
|
||||
createCategorySummaryFromDfCol = memoize(createCategorySummaryFromDfCol);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
getSelectionState(categorySummary: any) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoricalSelection' does not exist on ... Remove this comment to see the full error message
|
||||
const { categoricalSelection, metadataField } = this.props;
|
||||
return Category.getSelectionState(
|
||||
categoricalSelection,
|
||||
metadataField,
|
||||
categorySummary
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleColorChange = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "color by categorical metadata",
|
||||
colorAccessor: metadataField,
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleCategoryClick = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annotations' does not exist on type 'Rea... Remove this comment to see the full error message
|
||||
const { annotations, metadataField, onExpansionChange } = this.props;
|
||||
const editingCategory =
|
||||
annotations.isEditingCategoryName &&
|
||||
annotations.categoryBeingEdited === metadataField;
|
||||
if (!editingCategory) {
|
||||
onExpansionChange(metadataField);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleCategoryKeyPress = (e: any) => {
|
||||
if (e.key === "Enter") {
|
||||
this.handleCategoryClick();
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleToggleAllClick = (categorySummary: any) => {
|
||||
const isChecked = this.getSelectionState(categorySummary);
|
||||
if (isChecked === "all") {
|
||||
this.toggleNone(categorySummary);
|
||||
} else {
|
||||
this.toggleAll(categorySummary);
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
fetchAsyncProps = async (props: any) => {
|
||||
const { annoMatrix, metadataField, colors } = props.watchProps;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'crossfilter' does not exist on type 'Rea... Remove this comment to see the full error message
|
||||
const { crossfilter } = this.props;
|
||||
|
||||
const [categoryData, categorySummary, colorData] = await this.fetchData(
|
||||
annoMatrix,
|
||||
metadataField,
|
||||
colors
|
||||
);
|
||||
|
||||
return {
|
||||
categoryData,
|
||||
categorySummary,
|
||||
colorData,
|
||||
crossfilter,
|
||||
...this.updateColorTable(colorData),
|
||||
handleCategoryToggleAllClick: () =>
|
||||
this.handleToggleAllClick(categorySummary),
|
||||
};
|
||||
};
|
||||
|
||||
async fetchData(
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
annoMatrix: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
metadataField: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
colors: any
|
||||
): Promise<
|
||||
[
|
||||
Dataframe,
|
||||
ReturnType<typeof createCategorySummaryFromDfCol>,
|
||||
Dataframe | null
|
||||
]
|
||||
> {
|
||||
/*
|
||||
fetch our data and the color-by data if appropriate, and then build a summary
|
||||
of our category and a color table for the color-by annotation.
|
||||
*/
|
||||
const { schema } = annoMatrix;
|
||||
const { colorAccessor, colorMode } = colors;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'genesets' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { genesets } = this.props;
|
||||
let colorDataPromise: Promise<Dataframe | null> = Promise.resolve(null);
|
||||
if (colorAccessor) {
|
||||
const query = createColorQuery(
|
||||
colorMode,
|
||||
colorAccessor,
|
||||
schema,
|
||||
genesets
|
||||
);
|
||||
if (query) colorDataPromise = annoMatrix.fetch(...query);
|
||||
}
|
||||
const [categoryData, colorData] = await Promise.all<
|
||||
Dataframe,
|
||||
Dataframe | null
|
||||
>([annoMatrix.fetch("obs", metadataField), colorDataPromise]);
|
||||
|
||||
// our data
|
||||
const column = categoryData.icol(0);
|
||||
const colSchema = schema.annotations.obsByName[metadataField];
|
||||
const categorySummary = this.createCategorySummaryFromDfCol(
|
||||
column,
|
||||
colSchema
|
||||
);
|
||||
return [categoryData, categorySummary, colorData];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -- - FIXME: disabled temporarily on migrate to TS.
|
||||
updateColorTable(colorData: Dataframe|null) {
|
||||
// color table, which may be null
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
const { schema, colors, metadataField } = this.props;
|
||||
const { colorAccessor, userColors, colorMode } = colors;
|
||||
return {
|
||||
isColorAccessor: colorAccessor === metadataField,
|
||||
colorAccessor,
|
||||
colorMode,
|
||||
colorTable: createColorTable(
|
||||
colorMode,
|
||||
colorAccessor,
|
||||
colorData,
|
||||
schema,
|
||||
userColors
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
toggleNone(categorySummary: any) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch(
|
||||
actions.selectCategoricalAllMetadataAction(
|
||||
"categorical metadata filter none of these",
|
||||
metadataField,
|
||||
categorySummary.allCategoryValues,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
toggleAll(categorySummary: any) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch(
|
||||
actions.selectCategoricalAllMetadataAction(
|
||||
"categorical metadata filter all of these",
|
||||
metadataField,
|
||||
categorySummary.allCategoryValues,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isExpanded' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
isExpanded,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoricalSelection' does not exist on ... Remove this comment to see the full error message
|
||||
categoricalSelection,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'crossfilter' does not exist on type 'Rea... Remove this comment to see the full error message
|
||||
crossfilter,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colors' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
colors,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
annoMatrix,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isUserAnno' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
isUserAnno,
|
||||
} = this.props;
|
||||
|
||||
const checkboxID = `category-select-${metadataField}`;
|
||||
|
||||
return (
|
||||
<CategoryCrossfilterContext.Provider value={crossfilter}>
|
||||
<Async
|
||||
watchFn={Category.watchAsync}
|
||||
promiseFn={this.fetchAsyncProps}
|
||||
watchProps={{
|
||||
metadataField,
|
||||
annoMatrix,
|
||||
categoricalSelection,
|
||||
colors,
|
||||
}}
|
||||
>
|
||||
<Async.Pending initial>
|
||||
<StillLoading
|
||||
metadataField={metadataField}
|
||||
checkboxID={checkboxID}
|
||||
/>
|
||||
</Async.Pending>
|
||||
<Async.Rejected>
|
||||
{(error) => (
|
||||
<ErrorLoading metadataField={metadataField} error={error} />
|
||||
)}
|
||||
</Async.Rejected>
|
||||
<Async.Fulfilled persist>
|
||||
{(asyncProps) => {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'u... Remove this comment to see the full error message
|
||||
colorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type 'unkn... Remove this comment to see the full error message
|
||||
colorTable,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type 'unkno... Remove this comment to see the full error message
|
||||
colorData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type 'un... Remove this comment to see the full error message
|
||||
categoryData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categorySummary' does not exist on type ... Remove this comment to see the full error message
|
||||
categorySummary,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isColorAccessor' does not exist on type ... Remove this comment to see the full error message
|
||||
isColorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'handleCategoryToggleAllClick' does not e... Remove this comment to see the full error message
|
||||
handleCategoryToggleAllClick,
|
||||
} = asyncProps;
|
||||
const selectionState = this.getSelectionState(categorySummary);
|
||||
return (
|
||||
<CategoryRender
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ metadataField: any; checkboxID: string; is... Remove this comment to see the full error message
|
||||
metadataField={metadataField}
|
||||
checkboxID={checkboxID}
|
||||
isUserAnno={isUserAnno}
|
||||
isExpanded={isExpanded}
|
||||
isColorAccessor={isColorAccessor}
|
||||
selectionState={selectionState}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
onColorChangeClick={this.handleColorChange}
|
||||
onCategoryToggleAllClick={handleCategoryToggleAllClick}
|
||||
onCategoryMenuClick={this.handleCategoryClick}
|
||||
onCategoryMenuKeyPress={this.handleCategoryKeyPress}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Async.Fulfilled>
|
||||
</Async>
|
||||
</CategoryCrossfilterContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Category;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const StillLoading = ({ metadataField, checkboxID }: any) => (
|
||||
/*
|
||||
We are still loading this category, so render a "busy" signal.
|
||||
*/
|
||||
<div
|
||||
style={{
|
||||
maxWidth: globals.maxControlsWidth,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
htmlFor={checkboxID}
|
||||
className={`${Classes.CONTROL} ${Classes.CHECKBOX}`}
|
||||
>
|
||||
<input disabled id={checkboxID} checked type="checkbox" />
|
||||
<span className={Classes.CONTROL_INDICATOR} />
|
||||
</label>
|
||||
<Truncate>
|
||||
<span
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
width: LABEL_WIDTH,
|
||||
}}
|
||||
>
|
||||
{metadataField}
|
||||
</span>
|
||||
</Truncate>
|
||||
</div>
|
||||
<div>
|
||||
<Button minimal loading intent="primary" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const ErrorLoading = ({ metadataField, error }: any) => {
|
||||
console.error(error); // log error to console as it is unexpected.
|
||||
return (
|
||||
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
||||
<span
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
width: LABEL_WIDTH,
|
||||
fontStyle: "italic",
|
||||
}}
|
||||
>
|
||||
{`Failure loading ${metadataField}`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CategoryHeader = React.memo(
|
||||
({
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type '{... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'checkboxID' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
checkboxID,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isUserAnno' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
isUserAnno,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isColorAccessor' does not exist on type ... Remove this comment to see the full error message
|
||||
isColorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isExpanded' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
isExpanded,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'selectionState' does not exist on type '... Remove this comment to see the full error message
|
||||
selectionState,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onColorChangeClick' does not exist on ty... Remove this comment to see the full error message
|
||||
onColorChangeClick,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCategoryMenuClick' does not exist on t... Remove this comment to see the full error message
|
||||
onCategoryMenuClick,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCategoryMenuKeyPress' does not exist o... Remove this comment to see the full error message
|
||||
onCategoryMenuKeyPress,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCategoryToggleAllClick' does not exist... Remove this comment to see the full error message
|
||||
onCategoryToggleAllClick,
|
||||
}) => {
|
||||
/*
|
||||
Render category name and controls (eg, color-by button).
|
||||
*/
|
||||
const checkboxRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||
checkboxRef.current.indeterminate = selectionState === "some";
|
||||
}, [checkboxRef.current, selectionState]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label
|
||||
className={`${Classes.CONTROL} ${Classes.CHECKBOX}`}
|
||||
htmlFor={checkboxID}
|
||||
>
|
||||
<input
|
||||
id={checkboxID}
|
||||
data-testclass="category-select"
|
||||
data-testid={`${metadataField}:category-select`}
|
||||
onChange={onCategoryToggleAllClick}
|
||||
ref={checkboxRef}
|
||||
checked={selectionState === "all"}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span className={Classes.CONTROL_INDICATOR} />
|
||||
</label>
|
||||
<span
|
||||
role="menuitem"
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'number | ... Remove this comment to see the full error message
|
||||
tabIndex="0"
|
||||
data-testclass="category-expand"
|
||||
data-testid={`${metadataField}:category-expand`}
|
||||
onKeyPress={onCategoryMenuKeyPress}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={onCategoryMenuClick}
|
||||
>
|
||||
<Truncate>
|
||||
<span
|
||||
style={{
|
||||
maxWidth: isUserAnno ? LABEL_WIDTH_ANNO : LABEL_WIDTH,
|
||||
}}
|
||||
data-testid={`${metadataField}:category-label`}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'number | ... Remove this comment to see the full error message
|
||||
tabIndex="-1"
|
||||
>
|
||||
{metadataField}
|
||||
</span>
|
||||
</Truncate>
|
||||
{isExpanded ? (
|
||||
<FaChevronDown
|
||||
data-testclass="category-expand-is-expanded"
|
||||
style={{ fontSize: 10, marginLeft: 5 }}
|
||||
/>
|
||||
) : (
|
||||
<FaChevronRight
|
||||
data-testclass="category-expand-is-not-expanded"
|
||||
style={{ fontSize: 10, marginLeft: 5 }}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ metadataField: any; }' is not assignable t... Remove this comment to see the full error message */}
|
||||
<AnnoDialogEditCategoryName metadataField={metadataField} />
|
||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ metadataField: any; }' is not assignable t... Remove this comment to see the full error message */}
|
||||
<AnnoDialogAddLabel metadataField={metadataField} />
|
||||
<div>
|
||||
<AnnoMenu
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ metadataField: any; isUserAnno: any; creat... Remove this comment to see the full error message
|
||||
metadataField={metadataField}
|
||||
isUserAnno={isUserAnno}
|
||||
createText="Add a new label to this category"
|
||||
editText="Edit this category's name"
|
||||
deleteText="Delete this category, all associated labels, and remove all cell assignments"
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
content="Use as color scale"
|
||||
position={Position.LEFT}
|
||||
usePortal
|
||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||
modifiers={{
|
||||
preventOverflow: { enabled: false },
|
||||
hide: { enabled: false },
|
||||
}}
|
||||
>
|
||||
<AnchorButton
|
||||
data-testclass="colorby"
|
||||
data-testid={`colorby-${metadataField}`}
|
||||
onClick={onColorChangeClick}
|
||||
active={isColorAccessor}
|
||||
intent={isColorAccessor ? "primary" : "none"}
|
||||
icon="tint"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const CategoryRender = React.memo(
|
||||
({
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type '{... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'checkboxID' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
checkboxID,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isUserAnno' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
isUserAnno,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isColorAccessor' does not exist on type ... Remove this comment to see the full error message
|
||||
isColorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isExpanded' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
isExpanded,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'selectionState' does not exist on type '... Remove this comment to see the full error message
|
||||
selectionState,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type '{ ... Remove this comment to see the full error message
|
||||
categoryData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categorySummary' does not exist on type ... Remove this comment to see the full error message
|
||||
categorySummary,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type '{... Remove this comment to see the full error message
|
||||
colorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type '{ chi... Remove this comment to see the full error message
|
||||
colorData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
colorTable,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onColorChangeClick' does not exist on ty... Remove this comment to see the full error message
|
||||
onColorChangeClick,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCategoryMenuClick' does not exist on t... Remove this comment to see the full error message
|
||||
onCategoryMenuClick,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCategoryMenuKeyPress' does not exist o... Remove this comment to see the full error message
|
||||
onCategoryMenuKeyPress,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCategoryToggleAllClick' does not exist... Remove this comment to see the full error message
|
||||
onCategoryToggleAllClick,
|
||||
}) => {
|
||||
/*
|
||||
Render the core of the category, including checkboxes, controls, etc.
|
||||
*/
|
||||
const { numCategoryValues } = categorySummary;
|
||||
const isSingularValue = !isUserAnno && numCategoryValues === 1;
|
||||
|
||||
if (isSingularValue) {
|
||||
/*
|
||||
Entire category has a single value, special case.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
Otherwise, our normal multi-layout layout
|
||||
*/
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: globals.maxControlsWidth,
|
||||
}}
|
||||
data-testclass="category"
|
||||
data-testid={`category-${metadataField}`}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline",
|
||||
}}
|
||||
>
|
||||
<CategoryHeader
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ metadataField: any; checkboxID: any; isUse... Remove this comment to see the full error message
|
||||
metadataField={metadataField}
|
||||
checkboxID={checkboxID}
|
||||
isUserAnno={isUserAnno}
|
||||
isExpanded={isExpanded}
|
||||
isColorAccessor={isColorAccessor}
|
||||
selectionState={selectionState}
|
||||
onColorChangeClick={onColorChangeClick}
|
||||
onCategoryToggleAllClick={onCategoryToggleAllClick}
|
||||
onCategoryMenuClick={onCategoryMenuClick}
|
||||
onCategoryMenuKeyPress={onCategoryMenuKeyPress}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginLeft: 26 }}>
|
||||
{
|
||||
/* values*/
|
||||
isExpanded ? (
|
||||
<CategoryValueList
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isUserAnno: any; metadataField: any; categ... Remove this comment to see the full error message
|
||||
isUserAnno={isUserAnno}
|
||||
metadataField={metadataField}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const CategoryValueList = React.memo(
|
||||
({
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isUserAnno' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
isUserAnno,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type '{... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type '{ ... Remove this comment to see the full error message
|
||||
categoryData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categorySummary' does not exist on type ... Remove this comment to see the full error message
|
||||
categorySummary,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type '{... Remove this comment to see the full error message
|
||||
colorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type '{ chi... Remove this comment to see the full error message
|
||||
colorData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type '{ ch... Remove this comment to see the full error message
|
||||
colorTable,
|
||||
}) => {
|
||||
const tuples = [...categorySummary.categoryValueIndices];
|
||||
|
||||
/*
|
||||
Render the value list. If this is a user annotation, we use a flipper
|
||||
animation, if read-only, we don't bother and save a few bits of perf.
|
||||
*/
|
||||
if (!isUserAnno) {
|
||||
return (
|
||||
<>
|
||||
{tuples.map(([value, index]) => (
|
||||
<Value
|
||||
key={value}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ key: any; isUserAnno: any; metadataField: ... Remove this comment to see the full error message
|
||||
isUserAnno={isUserAnno}
|
||||
metadataField={metadataField}
|
||||
categoryIndex={index}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* User annotation */
|
||||
const flipKey = tuples.map((t) => t[0]).join("");
|
||||
return (
|
||||
<Flipper flipKey={flipKey}>
|
||||
{tuples.map(([value, index]) => (
|
||||
<Flipped key={value} flipId={value}>
|
||||
<Value
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isUserAnno: any; metadataField: any; categ... Remove this comment to see the full error message
|
||||
isUserAnno={isUserAnno}
|
||||
metadataField={metadataField}
|
||||
categoryIndex={index}
|
||||
categoryData={categoryData}
|
||||
categorySummary={categorySummary}
|
||||
colorAccessor={colorAccessor}
|
||||
colorData={colorData}
|
||||
colorTable={colorTable}
|
||||
/>
|
||||
</Flipped>
|
||||
))}
|
||||
</Flipper>
|
||||
);
|
||||
}
|
||||
);
|
||||
+35
-59
@@ -10,23 +10,13 @@ import LabelInput from "../labelInput";
|
||||
import { labelPrompt } from "./labelUtil";
|
||||
import actions from "../../actions";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
type State = any;
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state) => ({
|
||||
writableCategoriesEnabled:
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(state as any).config?.parameters?.annotations ?? false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
schema: (state as any).annoMatrix?.schema,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
userInfo: (state as any).userInfo,
|
||||
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
|
||||
schema: state.annoMatrix?.schema,
|
||||
userInfo: state.userInfo,
|
||||
}))
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
class Categories extends React.Component<{}, State> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(props: {}) {
|
||||
class Categories extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
createAnnoModeActive: false,
|
||||
@@ -36,9 +26,7 @@ class Categories extends React.Component<{}, State> {
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleCreateUserAnno = (e: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
handleCreateUserAnno = (e) => {
|
||||
const { dispatch } = this.props;
|
||||
const { newCategoryText, categoryToDuplicate } = this.state;
|
||||
dispatch(
|
||||
@@ -55,12 +43,10 @@ class Categories extends React.Component<{}, State> {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleEnableAnnoMode = () => {
|
||||
this.setState({ createAnnoModeActive: true });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleDisableAnnoMode = () => {
|
||||
this.setState({
|
||||
createAnnoModeActive: false,
|
||||
@@ -69,58 +55,56 @@ class Categories extends React.Component<{}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleModalDuplicateCategorySelection = (d: any) => {
|
||||
handleModalDuplicateCategorySelection = (d) => {
|
||||
this.setState({ categoryToDuplicate: d });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
categoryNameError = (name: any) => {
|
||||
categoryNameError = (name) => {
|
||||
/*
|
||||
return false if this is a LEGAL/acceptable category name or NULL/empty string,
|
||||
or return an error type.
|
||||
*/
|
||||
return false if this is a LEGAL/acceptable category name or NULL/empty string,
|
||||
or return an error type.
|
||||
*/
|
||||
|
||||
/* allow empty string */
|
||||
if (name === "") return false;
|
||||
|
||||
/*
|
||||
test for uniqueness against *all* annotation names, not just the subset
|
||||
we render as categorical.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
test for uniqueness against *all* annotation names, not just the subset
|
||||
we render as categorical.
|
||||
*/
|
||||
const { schema } = this.props;
|
||||
const allCategoryNames = schema.annotations.obs.columns.map(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(c: any) => c.name
|
||||
);
|
||||
const allCategoryNames = schema.annotations.obs.columns.map((c) => c.name);
|
||||
|
||||
/* check category name syntax */
|
||||
const error = AnnotationsHelpers.annotationNameIsErroneous(name);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
/* disallow duplicates */
|
||||
if (allCategoryNames.indexOf(name) !== -1) {
|
||||
return "duplicate";
|
||||
}
|
||||
|
||||
/* otherwise, no error */
|
||||
return false;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleChange = (name: any) => {
|
||||
handleChange = (name) => {
|
||||
this.setState({ newCategoryText: name });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleSelect = (name: any) => {
|
||||
handleSelect = (name) => {
|
||||
this.setState({ newCategoryText: name });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
instruction = (name: any) =>
|
||||
labelPrompt(this.categoryNameError(name), "New, unique category name", ":");
|
||||
instruction = (name) => labelPrompt(
|
||||
this.categoryNameError(name),
|
||||
"New, unique category name",
|
||||
":"
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
onExpansionChange = (catName: any) => {
|
||||
onExpansionChange = (catName) => {
|
||||
const { expandedCats } = this.state;
|
||||
if (expandedCats.has(catName)) {
|
||||
const _expandedCats = new Set(expandedCats);
|
||||
@@ -133,7 +117,6 @@ class Categories extends React.Component<{}, State> {
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const {
|
||||
createAnnoModeActive,
|
||||
@@ -141,13 +124,11 @@ class Categories extends React.Component<{}, State> {
|
||||
newCategoryText,
|
||||
expandedCats,
|
||||
} = this.state;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'writableCategoriesEnabled' does not exis... Remove this comment to see the full error message
|
||||
const { writableCategoriesEnabled, schema, userInfo } = this.props;
|
||||
/* all names, sorted in display order. Will be rendered in this order */
|
||||
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
|
||||
const allCategoryNames = ControlsHelpers.selectableCategoryNames(
|
||||
schema
|
||||
).sort();
|
||||
const allCategoryNames =
|
||||
ControlsHelpers.selectableCategoryNames(schema).sort();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -155,7 +136,6 @@ class Categories extends React.Component<{}, State> {
|
||||
}}
|
||||
>
|
||||
<AnnoDialog
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isActive: any; title: string; instruction:... Remove this comment to see the full error message
|
||||
isActive={createAnnoModeActive}
|
||||
title="Create new category"
|
||||
instruction={this.instruction(newCategoryText)}
|
||||
@@ -168,7 +148,6 @@ class Categories extends React.Component<{}, State> {
|
||||
handleCancel={this.handleDisableAnnoMode}
|
||||
annoInput={
|
||||
<LabelInput
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ labelSuggestions: null; onChange: (name: a... Remove this comment to see the full error message
|
||||
labelSuggestions={null}
|
||||
onChange={this.handleChange}
|
||||
onSelect={this.handleSelect}
|
||||
@@ -183,7 +162,6 @@ class Categories extends React.Component<{}, State> {
|
||||
}
|
||||
annoSelect={
|
||||
<AnnoSelect
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ handleModalDuplicateCategorySelection: (d:... Remove this comment to see the full error message
|
||||
handleModalDuplicateCategorySelection={
|
||||
this.handleModalDuplicateCategorySelection
|
||||
}
|
||||
@@ -192,6 +170,7 @@ class Categories extends React.Component<{}, State> {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{writableCategoriesEnabled ? (
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Tooltip
|
||||
@@ -220,16 +199,15 @@ class Categories extends React.Component<{}, State> {
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* READ ONLY CATEGORICAL FIELDS */}
|
||||
{/* this is duplicative but flat, could be abstracted */}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS */}
|
||||
{allCategoryNames.map((catName: any) =>
|
||||
{allCategoryNames.map((catName) =>
|
||||
!schema.annotations.obsByName[catName].writable &&
|
||||
(schema.annotations.obsByName[catName].categories?.length > 1 ||
|
||||
!schema.annotations.obsByName[catName].categories) ? (
|
||||
<Category
|
||||
key={catName}
|
||||
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||
metadataField={catName}
|
||||
onExpansionChange={this.onExpansionChange}
|
||||
isExpanded={expandedCats.has(catName)}
|
||||
@@ -238,12 +216,10 @@ class Categories extends React.Component<{}, State> {
|
||||
) : null
|
||||
)}
|
||||
{/* WRITEABLE FIELDS */}
|
||||
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS */}
|
||||
{allCategoryNames.map((catName: any) =>
|
||||
{allCategoryNames.map((catName) =>
|
||||
schema.annotations.obsByName[catName].writable ? (
|
||||
<Category
|
||||
key={catName}
|
||||
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||
metadataField={catName}
|
||||
onExpansionChange={this.onExpansionChange}
|
||||
isExpanded={expandedCats.has(catName)}
|
||||
+2
-5
@@ -3,8 +3,7 @@ import { Colors } from "@blueprintjs/core";
|
||||
|
||||
import { AnnotationsHelpers } from "../../util/stateManager";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function isLabelErroneous(label: any, metadataField: any, schema: any) {
|
||||
export function isLabelErroneous(label, metadataField, schema) {
|
||||
/*
|
||||
return false if this is a LEGAL/acceptable category name or NULL/empty string,
|
||||
or return an error type.
|
||||
@@ -36,11 +35,9 @@ const errorMessageMap = {
|
||||
"multi-space-run": "Multiple consecutive spaces not allowed",
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function labelPrompt(err: any, prolog: any, epilog: any) {
|
||||
export function labelPrompt(err, prolog, epilog) {
|
||||
let errPrompt = null;
|
||||
if (err) {
|
||||
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||
let errMsg = errorMessageMap[err] ?? "error";
|
||||
errMsg = errMsg[0].toLowerCase() + errMsg.slice(1);
|
||||
errPrompt = (
|
||||
+46
-163
@@ -13,7 +13,6 @@ import {
|
||||
Position,
|
||||
} from "@blueprintjs/core";
|
||||
import * as globals from "../../../globals";
|
||||
// @ts-expect-error ts-migrate(2307) FIXME: Cannot find module '../categorical.css' or its cor... Remove this comment to see the full error message
|
||||
import styles from "../categorical.css";
|
||||
import AnnoDialog from "../../annoDialog";
|
||||
import LabelInput from "../../labelInput";
|
||||
@@ -25,28 +24,20 @@ import actions from "../../../actions";
|
||||
import MiniHistogram from "../../miniHistogram";
|
||||
import MiniStackedBar from "../../miniStackedBar";
|
||||
import { CategoryCrossfilterContext } from "../categoryContext";
|
||||
import { Dataframe, ContinuousHistogram } from "../../../util/dataframe";
|
||||
|
||||
const STACKED_BAR_HEIGHT = 11;
|
||||
const STACKED_BAR_WIDTH = 100;
|
||||
|
||||
/* this is defined outside of the class so we can use it in connect() */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
function _currentLabelAsString(ownProps: any) {
|
||||
function _currentLabelAsString(ownProps) {
|
||||
const { label } = ownProps;
|
||||
// when called as a function, the String() constructor performs type conversion,
|
||||
// and returns a primitive string.
|
||||
return String(label);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
type State = any;
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state, ownProps) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'pointDilation' does not exist on type 'D... Remove this comment to see the full error message
|
||||
const { pointDilation, categoricalSelection } = state;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type '{... Remove this comment to see the full error message
|
||||
const { metadataField, categorySummary, categoryIndex } = ownProps;
|
||||
const isDilated =
|
||||
pointDilation.metadataField === metadataField &&
|
||||
@@ -57,35 +48,27 @@ type State = any;
|
||||
const isSelected = category.get(label) ?? true;
|
||||
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
annotations: (state as any).annotations,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
schema: (state as any).annoMatrix?.schema,
|
||||
annotations: state.annotations,
|
||||
schema: state.annoMatrix?.schema,
|
||||
isDilated,
|
||||
isSelected,
|
||||
label,
|
||||
};
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
class CategoryValue extends React.Component<{}, State> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(props: {}) {
|
||||
class CategoryValue extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
editedLabelText: this.currentLabelAsString(),
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
componentDidUpdate(prevProps: {}) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
componentDidUpdate(prevProps) {
|
||||
const { metadataField, categoryIndex, categorySummary } = this.props;
|
||||
if (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(prevProps as any).metadataField !== metadataField ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(prevProps as any).categoryIndex !== categoryIndex || // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(prevProps as any).categorySummary !== categorySummary
|
||||
prevProps.metadataField !== metadataField ||
|
||||
prevProps.categoryIndex !== categoryIndex ||
|
||||
prevProps.categorySummary !== categorySummary
|
||||
) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state --- adequately checked to prevent looping
|
||||
this.setState({
|
||||
@@ -95,31 +78,23 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
}
|
||||
|
||||
// If coloring by and this isn't the colorAccessor and it isn't being edited
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
get shouldRenderStackedBarOrHistogram() {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { colorAccessor, isColorBy, annotations } = this.props;
|
||||
|
||||
return !!colorAccessor && !isColorBy && !annotations.isEditingLabelName;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleDeleteValue = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField, label } = this.props;
|
||||
dispatch(actions.annotationDeleteLabelFromCategory(metadataField, label));
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleAddCurrentSelectionToThisLabel = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField, label } = this.props;
|
||||
dispatch(actions.annotationLabelCurrentSelection(metadataField, label));
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleEditValue = (e: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
handleEditValue = (e) => {
|
||||
const { dispatch, metadataField, label } = this.props;
|
||||
const { editedLabelText } = this.state;
|
||||
this.cancelEditMode();
|
||||
@@ -133,9 +108,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleCreateArbitraryLabel = (txt: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
handleCreateArbitraryLabel = (txt) => {
|
||||
const { dispatch, metadataField, label } = this.props;
|
||||
this.cancelEditMode();
|
||||
dispatch(
|
||||
@@ -143,21 +116,15 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
labelNameError = (name: any) => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
labelNameError = (name) => {
|
||||
const { metadataField, schema } = this.props;
|
||||
if (name === this.currentLabelAsString()) return false;
|
||||
return isLabelErroneous(name, metadataField, schema);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
instruction = (label: any) =>
|
||||
labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
instruction = (label) => labelPrompt(this.labelNameError(label), "New, unique label", ":");
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
activateEditLabelMode = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField, categoryIndex, label } = this.props;
|
||||
dispatch({
|
||||
type: "annotation: activate edit label mode",
|
||||
@@ -167,9 +134,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
cancelEditMode = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField, categoryIndex, label } = this.props;
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabelAsString(),
|
||||
@@ -182,18 +147,9 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
toggleOff = () => {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
dispatch,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryIndex' does not exist on type 'R... Remove this comment to see the full error message
|
||||
categoryIndex,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categorySummary' does not exist on type ... Remove this comment to see the full error message
|
||||
categorySummary,
|
||||
} = this.props;
|
||||
const { dispatch, metadataField, categoryIndex, categorySummary } =
|
||||
this.props;
|
||||
const label = categorySummary.categoryValues[categoryIndex];
|
||||
dispatch(
|
||||
actions.selectCategoricalMetadataAction(
|
||||
@@ -206,8 +162,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
shouldComponentUpdate = (nextProps: any, nextState: any) => {
|
||||
shouldComponentUpdate = (nextProps, nextState) => {
|
||||
/*
|
||||
Checks to see if at least one of the following changed:
|
||||
* world state
|
||||
@@ -218,7 +173,6 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
If and only if true, update the component
|
||||
*/
|
||||
const { props, state } = this;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryIndex' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { categoryIndex, categorySummary, isSelected } = props;
|
||||
const {
|
||||
categoryIndex: newCategoryIndex,
|
||||
@@ -231,15 +185,10 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
const labelChanged = label !== newLabel;
|
||||
const valueSelectionChange = isSelected !== newIsSelected;
|
||||
|
||||
const colorAccessorChange =
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(props as any).colorAccessor !== nextProps.colorAccessor;
|
||||
const annotationsChange =
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(props as any).annotations !== nextProps.annotations;
|
||||
const colorAccessorChange = props.colorAccessor !== nextProps.colorAccessor;
|
||||
const annotationsChange = props.annotations !== nextProps.annotations;
|
||||
const editingLabel = state.editedLabelText !== nextState.editedLabelText;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const dilationChange = (props as any).isDilated !== nextProps.isDilated;
|
||||
const dilationChange = props.isDilated !== nextProps.isDilated;
|
||||
|
||||
const count = categorySummary.categoryValueCounts[categoryIndex];
|
||||
const newCount = newCategorySummary.categoryValueCounts[newCategoryIndex];
|
||||
@@ -250,8 +199,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
// if any one changes, but only for the currently colored-by category.
|
||||
const colorMightHaveChanged =
|
||||
nextProps.colorAccessor === nextProps.metadataField &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(props as any).categorySummary !== nextProps.categorySummary;
|
||||
props.categorySummary !== nextProps.categorySummary;
|
||||
|
||||
return (
|
||||
labelChanged ||
|
||||
@@ -265,18 +213,9 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
toggleOn = () => {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
dispatch,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryIndex' does not exist on type 'R... Remove this comment to see the full error message
|
||||
categoryIndex,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categorySummary' does not exist on type ... Remove this comment to see the full error message
|
||||
categorySummary,
|
||||
} = this.props;
|
||||
const { dispatch, metadataField, categoryIndex, categorySummary } =
|
||||
this.props;
|
||||
const label = categorySummary.categoryValues[categoryIndex];
|
||||
dispatch(
|
||||
actions.selectCategoricalMetadataAction(
|
||||
@@ -289,9 +228,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleMouseEnter = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField, categoryIndex, label } = this.props;
|
||||
dispatch({
|
||||
type: "category value mouse hover start",
|
||||
@@ -301,9 +238,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
handleMouseExit = () => {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||
const { dispatch, metadataField, categoryIndex, label } = this.props;
|
||||
dispatch({
|
||||
type: "category value mouse hover end",
|
||||
@@ -313,32 +248,23 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleTextChange = (text: any) => {
|
||||
handleTextChange = (text) => {
|
||||
this.setState({ editedLabelText: text });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
handleChoice = (e: any) => {
|
||||
handleChoice = (e) => {
|
||||
/* Blueprint Suggest format */
|
||||
this.setState({ editedLabelText: e.target });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
createHistogramBins = (
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
metadataField: any,
|
||||
categoryData: Dataframe,
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'colorAccessor' is declared but its value is never... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
colorAccessor: any,
|
||||
colorData: Dataframe,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
categoryValue: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
width: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
height: any
|
||||
metadataField,
|
||||
categoryData,
|
||||
colorAccessor,
|
||||
colorData,
|
||||
categoryValue,
|
||||
width,
|
||||
height
|
||||
) => {
|
||||
/*
|
||||
Knowing that colorScale is based off continuous data,
|
||||
@@ -347,17 +273,17 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
*/
|
||||
const groupBy = categoryData.col(metadataField);
|
||||
const col = colorData.icol(0);
|
||||
const range = col.summarizeContinuous();
|
||||
const range = col.summarize();
|
||||
|
||||
const histogramMap = col.histogramContinuousBy(
|
||||
const histogramMap = col.histogram(
|
||||
50,
|
||||
[range.min, range.max],
|
||||
groupBy
|
||||
);
|
||||
); /* Because the signature changes we really need different names for histogram to differentiate signatures */
|
||||
|
||||
const bins = histogramMap.has(categoryValue)
|
||||
? histogramMap.get(categoryValue) as ContinuousHistogram
|
||||
: new Array<number>(50).fill(0);
|
||||
? histogramMap.get(categoryValue)
|
||||
: new Array(50).fill(0);
|
||||
|
||||
const xScale = d3.scaleLinear().domain([0, bins.length]).range([0, width]);
|
||||
|
||||
@@ -372,23 +298,15 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
};
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
createStackedGraphBins = (
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
metadataField: any,
|
||||
categoryData: Dataframe,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
colorAccessor: any,
|
||||
colorData: Dataframe,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
categoryValue: any,
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'colorTable' is declared but its value is never re... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
colorTable: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
schema: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
width: any
|
||||
metadataField,
|
||||
categoryData,
|
||||
colorAccessor,
|
||||
colorData,
|
||||
categoryValue,
|
||||
colorTable,
|
||||
schema,
|
||||
width
|
||||
) => {
|
||||
/*
|
||||
Knowing that the color scale is based off of categorical data,
|
||||
@@ -398,7 +316,7 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
const groupBy = categoryData.col(metadataField);
|
||||
const occupancyMap = colorData
|
||||
.col(colorAccessor)
|
||||
.histogramCategoricalBy(groupBy);
|
||||
.histogramCategorical(groupBy);
|
||||
|
||||
const occupancy = occupancyMap.get(categoryValue);
|
||||
|
||||
@@ -425,19 +343,16 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
return null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
currentLabelAsString() {
|
||||
return _currentLabelAsString(this.props);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
isAddCurrentSelectionDisabled(crossfilter: any, category: any, value: any) {
|
||||
isAddCurrentSelectionDisabled(crossfilter, category, value) {
|
||||
/*
|
||||
disable "add current selection to label", if one of the following is true:
|
||||
1. no cells are selected
|
||||
2. all currently selected cells already have this label, on this category
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type 'Re... Remove this comment to see the full error message
|
||||
const { categoryData } = this.props;
|
||||
|
||||
// 1. no cells selected?
|
||||
@@ -455,22 +370,14 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
renderMiniStackedBar = () => {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'R... Remove this comment to see the full error message
|
||||
colorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type 'Re... Remove this comment to see the full error message
|
||||
categoryData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type 'Reado... Remove this comment to see the full error message
|
||||
colorData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
colorTable,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
schema,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'label' does not exist on type 'Readonly<... Remove this comment to see the full error message
|
||||
label,
|
||||
} = this.props;
|
||||
const isColorBy = metadataField === colorAccessor;
|
||||
@@ -508,29 +415,20 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
domain,
|
||||
occupancy,
|
||||
}}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ height: number; width: number; colorTable:... Remove this comment to see the full error message
|
||||
height={STACKED_BAR_HEIGHT}
|
||||
width={STACKED_BAR_WIDTH}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
renderMiniHistogram = () => {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'R... Remove this comment to see the full error message
|
||||
colorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type 'Reado... Remove this comment to see the full error message
|
||||
colorData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type 'Re... Remove this comment to see the full error message
|
||||
categoryData,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
colorTable,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||
schema,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'label' does not exist on type 'Readonly<... Remove this comment to see the full error message
|
||||
label,
|
||||
} = this.props;
|
||||
const colorScale = colorTable?.scale;
|
||||
@@ -565,7 +463,6 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
yScale,
|
||||
bins,
|
||||
}}
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ obsOrVarContinuousFieldDisplayName: any; d... Remove this comment to see the full error message
|
||||
obsOrVarContinuousFieldDisplayName={colorAccessor}
|
||||
domainLabel={label}
|
||||
height={STACKED_BAR_HEIGHT}
|
||||
@@ -574,28 +471,17 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
const {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryIndex' does not exist on type 'R... Remove this comment to see the full error message
|
||||
categoryIndex,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'R... Remove this comment to see the full error message
|
||||
colorAccessor,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
colorTable,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isUserAnno' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
isUserAnno,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annotations' does not exist on type 'Rea... Remove this comment to see the full error message
|
||||
annotations,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isDilated' does not exist on type 'Reado... Remove this comment to see the full error message
|
||||
isDilated,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isSelected' does not exist on type 'Read... Remove this comment to see the full error message
|
||||
isSelected,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categorySummary' does not exist on type ... Remove this comment to see the full error message
|
||||
categorySummary,
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'label' does not exist on type 'Readonly<... Remove this comment to see the full error message
|
||||
label,
|
||||
} = this.props;
|
||||
const colorScale = colorTable?.scale;
|
||||
@@ -691,7 +577,6 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
<span
|
||||
data-testid={`categorical-value-${metadataField}-${displayString}`}
|
||||
data-testclass="categorical-value"
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'number | ... Remove this comment to see the full error message
|
||||
tabIndex="-1"
|
||||
style={{
|
||||
width: labelWidth,
|
||||
@@ -717,7 +602,6 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
{editModeActive ? (
|
||||
<div>
|
||||
<AnnoDialog
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isActive: any; inputProps: { "data-testid"... Remove this comment to see the full error message
|
||||
isActive={editModeActive}
|
||||
inputProps={{
|
||||
"data-testid": `${metadataField}:edit-label-name-dialog`,
|
||||
@@ -736,7 +620,6 @@ class CategoryValue extends React.Component<{}, State> {
|
||||
handleCancel={this.cancelEditMode}
|
||||
annoInput={
|
||||
<LabelInput
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ label: any; labelSuggestions: null; onChan... Remove this comment to see the full error message
|
||||
label={editedLabelText}
|
||||
labelSuggestions={null}
|
||||
onChange={this.handleTextChange}
|
||||
+41
-49
@@ -8,62 +8,60 @@ import {
|
||||
Position,
|
||||
} from "@blueprintjs/core";
|
||||
|
||||
import { Dataframe } from "../../../util/dataframe";
|
||||
|
||||
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
|
||||
@connect((state) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
schema: (state as any).annoMatrix?.schema,
|
||||
schema: state.annoMatrix?.schema,
|
||||
}))
|
||||
class Occupancy extends React.PureComponent {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
canvas: any;
|
||||
|
||||
_WIDTH = 100;
|
||||
|
||||
_HEIGHT = 11;
|
||||
|
||||
createHistogram = (): void => {
|
||||
createHistogram = () => {
|
||||
/*
|
||||
Knowing that colorScale is based off continuous data,
|
||||
createHistogram fetches the continuous data in relation to the cells relevant to the category value.
|
||||
It then separates that data into 50 bins for drawing the mini-histogram
|
||||
*/
|
||||
const { metadataField, categoryData, colorData, categoryValue } = this
|
||||
.props as {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
|
||||
metadataField;
|
||||
categoryData: Dataframe;
|
||||
colorData: Dataframe;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryValue' does not exist on type 'R... Remove this comment to see the full error message
|
||||
categoryValue;
|
||||
};
|
||||
Knowing that colorScale is based off continous data,
|
||||
createHistogram fetches the continous data in relation to the cells releveant to the catagory value.
|
||||
It then seperates that data into 50 bins for drawing the mini-histogram
|
||||
*/
|
||||
const { metadataField, categoryData, colorData, categoryValue } =
|
||||
this.props;
|
||||
|
||||
if (!this.canvas) return;
|
||||
|
||||
const groupBy = categoryData.col(metadataField);
|
||||
const col = colorData.icol(0);
|
||||
const range = col.summarizeContinuous();
|
||||
const histogramMap = col.histogramContinuousBy(
|
||||
const range = col.summarize();
|
||||
|
||||
const histogramMap = col.histogram(
|
||||
50,
|
||||
[range.min, range.max],
|
||||
groupBy
|
||||
);
|
||||
); /* Because the signature changes we really need different names for histogram to differentiate signatures */
|
||||
|
||||
const bins = histogramMap.has(categoryValue)
|
||||
? (histogramMap.get(categoryValue) as number[])
|
||||
? histogramMap.get(categoryValue)
|
||||
: new Array(50).fill(0);
|
||||
|
||||
const xScale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, bins.length])
|
||||
.range([0, this._WIDTH]);
|
||||
|
||||
const largestBin = Math.max(...bins);
|
||||
|
||||
const yScale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, largestBin])
|
||||
.range([0, this._HEIGHT]);
|
||||
|
||||
const ctx = this.canvas.getContext("2d");
|
||||
|
||||
ctx.fillStyle = "#000";
|
||||
|
||||
let x;
|
||||
let y;
|
||||
|
||||
const rectWidth = this._WIDTH / bins.length;
|
||||
|
||||
for (let i = 0, { length } = bins; i < length; i += 1) {
|
||||
x = xScale(i);
|
||||
y = yScale(bins[i]);
|
||||
@@ -71,12 +69,12 @@ class Occupancy extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
createOccupancyStack = (): void => {
|
||||
createOccupancyStack = () => {
|
||||
/*
|
||||
Knowing that the color scale is based off of catagorical data,
|
||||
createOccupancyStack obtains a map showing the number if cells per colored value
|
||||
Using the colorScale a stack of colored bars is drawn representing the map
|
||||
*/
|
||||
Knowing that the color scale is based off of catagorical data,
|
||||
createOccupancyStack obtains a map showing the number if cells per colored value
|
||||
Using the colorScale a stack of colored bars is drawn representing the map
|
||||
*/
|
||||
const {
|
||||
metadataField,
|
||||
categoryData,
|
||||
@@ -85,28 +83,20 @@ class Occupancy extends React.PureComponent {
|
||||
colorTable,
|
||||
schema,
|
||||
colorData,
|
||||
} = this.props as {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
metadataField: any;
|
||||
categoryData: Dataframe;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
colorAccessor: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
categoryValue: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
colorTable: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
schema: any;
|
||||
colorData: Dataframe;
|
||||
};
|
||||
} = this.props;
|
||||
const { scale: colorScale } = colorTable;
|
||||
|
||||
const ctx = this.canvas?.getContext("2d");
|
||||
|
||||
if (!ctx) return;
|
||||
|
||||
const groupBy = categoryData.col(metadataField);
|
||||
const occupancyMap = colorData
|
||||
.col(colorAccessor)
|
||||
.histogramCategoricalBy(groupBy);
|
||||
.histogramCategorical(groupBy);
|
||||
|
||||
const occupancy = occupancyMap.get(categoryValue);
|
||||
|
||||
if (occupancy && occupancy.size > 0) {
|
||||
// not all categories have occupancy, so occupancy may be undefined.
|
||||
const x = d3
|
||||
@@ -116,15 +106,18 @@ class Occupancy extends React.PureComponent {
|
||||
.range([0, this._WIDTH]);
|
||||
const categories =
|
||||
schema.annotations.obsByName[colorAccessor]?.categories;
|
||||
|
||||
let currentOffset = 0;
|
||||
const dfColumn = colorData.col(colorAccessor);
|
||||
const categoryValues = dfColumn.summarizeCategorical().categories;
|
||||
|
||||
let o;
|
||||
let scaledValue;
|
||||
let value;
|
||||
|
||||
for (let i = 0, { length } = categoryValues; i < length; i += 1) {
|
||||
value = categoryValues[i];
|
||||
o = occupancy.get(value) as number;
|
||||
o = occupancy.get(value);
|
||||
scaledValue = x(o);
|
||||
ctx.fillStyle = o
|
||||
? colorScale(categories.indexOf(value))
|
||||
@@ -135,13 +128,12 @@ class Occupancy extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
render() {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'R... Remove this comment to see the full error message
|
||||
const { colorAccessor, categoryValue, colorByIsCategorical } = this.props;
|
||||
const { canvas } = this;
|
||||
if (canvas)
|
||||
canvas.getContext("2d").clearRect(0, 0, this._WIDTH, this._HEIGHT);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
interactionKind={PopoverInteractionKind.HOVER_TARGET_ONLY}
|
||||
Reference in New Issue
Block a user