mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-06 15:48:12 +08:00
Remove Continuous vars with 1 value from histogram, add to info drawer (#1927)
* remove single val continous metadata from histogram, add to info drawer * refactor to save singleContinuous values in state * fix edge case, single continuous values reappeard in rsb when clipped
This commit is contained in:
@@ -450,6 +450,7 @@ const Histogram = ({
|
|||||||
isScatterplotYYaccessor: state.controls.scatterplotYYaccessor === field,
|
isScatterplotYYaccessor: state.controls.scatterplotYYaccessor === field,
|
||||||
continuousSelectionRange: state.continuousSelection[myName],
|
continuousSelectionRange: state.continuousSelection[myName],
|
||||||
isColorAccessor: state.colors.colorAccessor === field,
|
isColorAccessor: state.colors.colorAccessor === field,
|
||||||
|
singleContinuousValues: state.singleContinuousValue.singleContinuousValues,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
class HistogramBrush extends React.PureComponent {
|
class HistogramBrush extends React.PureComponent {
|
||||||
@@ -608,18 +609,44 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchAsyncProps = async () => {
|
fetchAsyncProps = async () => {
|
||||||
const { annoMatrix } = this.props;
|
const { annoMatrix, field, dispatch, singleContinuousValues } = this.props;
|
||||||
const { isClipped } = annoMatrix;
|
const { isClipped } = annoMatrix;
|
||||||
|
if (singleContinuousValues.has(field)) {
|
||||||
|
return {
|
||||||
|
histogram: undefined,
|
||||||
|
range: undefined,
|
||||||
|
unclippedRange: undefined,
|
||||||
|
unclippedRangeColor: globals.blue,
|
||||||
|
isSingleValue: true,
|
||||||
|
OK2Render: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
const query = this.createQuery();
|
const query = this.createQuery();
|
||||||
const df = await annoMatrix.fetch(...query);
|
const df = await annoMatrix.fetch(...query);
|
||||||
const column = df.icol(0);
|
const column = df.icol(0);
|
||||||
|
|
||||||
// if we are clipped, fetch both our value and our unclipped value,
|
|
||||||
// as we need the absolute min/max range, not just the clipped min/max.
|
|
||||||
const summary = column.summarize();
|
const summary = column.summarize();
|
||||||
const range = [summary.min, summary.max];
|
const range = [summary.min, summary.max];
|
||||||
|
|
||||||
|
if (summary.min === summary.max && !isClipped) {
|
||||||
|
dispatch({
|
||||||
|
type: "add single continuous value",
|
||||||
|
field,
|
||||||
|
value: summary.min,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
histogram: undefined,
|
||||||
|
range,
|
||||||
|
unclippedRange: range,
|
||||||
|
unclippedRangeColor: globals.blue,
|
||||||
|
isSingleValue: true,
|
||||||
|
OK2Render: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSingleValue = summary.min === summary.max;
|
||||||
|
// if we are clipped, fetch both our value and our unclipped value,
|
||||||
|
// as we need the absolute min/max range, not just the clipped min/max.
|
||||||
let unclippedRange = [...range];
|
let unclippedRange = [...range];
|
||||||
if (isClipped) {
|
if (isClipped) {
|
||||||
const parent = await annoMatrix.viewOf.fetch(...query);
|
const parent = await annoMatrix.viewOf.fetch(...query);
|
||||||
@@ -643,7 +670,6 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
this.height
|
this.height
|
||||||
);
|
);
|
||||||
|
|
||||||
const isSingleValue = summary.min === summary.max;
|
|
||||||
const nonFiniteExtent =
|
const nonFiniteExtent =
|
||||||
summary.min === undefined ||
|
summary.min === undefined ||
|
||||||
summary.max === undefined ||
|
summary.max === undefined ||
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { PureComponent } from "react";
|
import React, { PureComponent } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect, shallowEqual } from "react-redux";
|
||||||
import { Drawer } from "@blueprintjs/core";
|
import { Drawer } from "@blueprintjs/core";
|
||||||
|
|
||||||
import InfoFormat from "./infoFormat";
|
import InfoFormat from "./infoFormat";
|
||||||
import { selectableCategoryNames } from "../../util/stateManager/controlsHelpers";
|
import { selectableCategoryNames } from "../../util/stateManager/controlsHelpers";
|
||||||
|
|
||||||
@@ -13,9 +12,14 @@ import { selectableCategoryNames } from "../../util/stateManager/controlsHelpers
|
|||||||
aboutURL: state.config?.links?.["about-dataset"],
|
aboutURL: state.config?.links?.["about-dataset"],
|
||||||
isOpen: state.controls.datasetDrawer,
|
isOpen: state.controls.datasetDrawer,
|
||||||
dataPortalProps: state.config?.["corpora_props"] ?? {},
|
dataPortalProps: state.config?.["corpora_props"] ?? {},
|
||||||
|
singleContinuousValues: state.singleContinuousValue.singleContinuousValues,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
class InfoDrawer extends PureComponent {
|
class InfoDrawer extends PureComponent {
|
||||||
|
static watchAsync(props, prevProps) {
|
||||||
|
return !shallowEqual(props.watchProps, prevProps.watchProps);
|
||||||
|
}
|
||||||
|
|
||||||
handleClose = () => {
|
handleClose = () => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
@@ -30,18 +34,22 @@ class InfoDrawer extends PureComponent {
|
|||||||
schema,
|
schema,
|
||||||
isOpen,
|
isOpen,
|
||||||
dataPortalProps,
|
dataPortalProps,
|
||||||
|
singleContinuousValues,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const allCategoryNames = selectableCategoryNames(schema).sort();
|
const allCategoryNames = selectableCategoryNames(schema).sort();
|
||||||
const singleValueCategories = new Map();
|
const allSingleValues = new Map();
|
||||||
|
|
||||||
allCategoryNames.forEach((catName) => {
|
allCategoryNames.forEach((catName) => {
|
||||||
const isUserAnno = schema?.annotations?.obsByName[catName]?.writable;
|
const isUserAnno = schema?.annotations?.obsByName[catName]?.writable;
|
||||||
const colSchema = schema.annotations.obsByName[catName];
|
const colSchema = schema.annotations.obsByName[catName];
|
||||||
if (!isUserAnno && colSchema.categories?.length === 1) {
|
if (!isUserAnno && colSchema.categories?.length === 1) {
|
||||||
singleValueCategories.set(catName, colSchema.categories[0]);
|
allSingleValues.set(catName, colSchema.categories[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
singleContinuousValues.forEach((value, catName) => {
|
||||||
|
allSingleValues.set(catName, value);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
@@ -53,7 +61,7 @@ class InfoDrawer extends PureComponent {
|
|||||||
{...{
|
{...{
|
||||||
datasetTitle,
|
datasetTitle,
|
||||||
aboutURL,
|
aboutURL,
|
||||||
singleValueCategories,
|
allSingleValues,
|
||||||
dataPortalProps,
|
dataPortalProps,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -85,13 +85,13 @@ const ONTOLOGY_KEY = "ontology_term_id";
|
|||||||
const CAT_WIDTH = "30%";
|
const CAT_WIDTH = "30%";
|
||||||
const VAL_WIDTH = "35%";
|
const VAL_WIDTH = "35%";
|
||||||
// Render list of metadata attributes found in categorical field
|
// Render list of metadata attributes found in categorical field
|
||||||
const renderSingleValueCategories = (singleValueCategories) => {
|
const renderSingleValues = (singleValues) => {
|
||||||
if (singleValueCategories.size === 0) return null;
|
if (singleValues.size === 0) return null;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<H3>Dataset Metadata</H3>
|
<H3>Dataset Metadata</H3>
|
||||||
<UL>
|
<UL>
|
||||||
{Array.from(singleValueCategories).reduce((elems, pair) => {
|
{Array.from(singleValues).reduce((elems, pair) => {
|
||||||
const [category, value] = pair;
|
const [category, value] = pair;
|
||||||
// If the value is empty skip it
|
// If the value is empty skip it
|
||||||
if (!value) return elems;
|
if (!value) return elems;
|
||||||
@@ -168,7 +168,7 @@ const renderLinks = (projectLinks, aboutURL) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const InfoFormat = React.memo(
|
const InfoFormat = React.memo(
|
||||||
({ datasetTitle, singleValueCategories, aboutURL, dataPortalProps = {} }) => {
|
({ datasetTitle, allSingleValues, aboutURL, dataPortalProps = {} }) => {
|
||||||
if (dataPortalProps.version?.["corpora_schema_version"] !== "1.0.0") {
|
if (dataPortalProps.version?.["corpora_schema_version"] !== "1.0.0") {
|
||||||
dataPortalProps = {};
|
dataPortalProps = {};
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ const InfoFormat = React.memo(
|
|||||||
{renderDOILink("DOI", doi)}
|
{renderDOILink("DOI", doi)}
|
||||||
{renderDOILink("Preprint DOI", preprintDOI)}
|
{renderDOILink("Preprint DOI", preprintDOI)}
|
||||||
{renderOrganism(organism)}
|
{renderOrganism(organism)}
|
||||||
{renderSingleValueCategories(singleValueCategories)}
|
{renderSingleValues(allSingleValues)}
|
||||||
{renderLinks(projectLinks, aboutURL)}
|
{renderLinks(projectLinks, aboutURL)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import centroidLabels from "./centroidLabels";
|
|||||||
import pointDialation from "./pointDilation";
|
import pointDialation from "./pointDilation";
|
||||||
import { reembedController } from "./reembed";
|
import { reembedController } from "./reembed";
|
||||||
import { gcMiddleware as annoMatrixGC } from "../annoMatrix";
|
import { gcMiddleware as annoMatrixGC } from "../annoMatrix";
|
||||||
|
import singleContinuousValue from "./singleContinuousValue";
|
||||||
import undoableConfig from "./undoableConfig";
|
import undoableConfig from "./undoableConfig";
|
||||||
|
|
||||||
const Reducer = undoable(
|
const Reducer = undoable(
|
||||||
@@ -32,6 +32,7 @@ const Reducer = undoable(
|
|||||||
["ontology", ontology],
|
["ontology", ontology],
|
||||||
["annotations", annotations],
|
["annotations", annotations],
|
||||||
["layoutChoice", layoutChoice],
|
["layoutChoice", layoutChoice],
|
||||||
|
["singleContinuousValue", singleContinuousValue],
|
||||||
["categoricalSelection", categoricalSelection],
|
["categoricalSelection", categoricalSelection],
|
||||||
["continuousSelection", continuousSelection],
|
["continuousSelection", continuousSelection],
|
||||||
["graphSelection", graphSelection],
|
["graphSelection", graphSelection],
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
const initialState = {
|
||||||
|
singleContinuousValues: new Map(),
|
||||||
|
};
|
||||||
|
const singleContinuousValue = (state = initialState, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case "add single continuous value":
|
||||||
|
state.singleContinuousValues.set(action.field, action.value);
|
||||||
|
return state;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default singleContinuousValue;
|
||||||
Reference in New Issue
Block a user