mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-09 04:28:11 +08:00
mini
This commit is contained in:
@@ -10,6 +10,7 @@ const HistogramFooter = React.memo(
|
|||||||
rangeColorMax,
|
rangeColorMax,
|
||||||
logFoldChange,
|
logFoldChange,
|
||||||
pvalAdj,
|
pvalAdj,
|
||||||
|
isObs,
|
||||||
}) => {
|
}) => {
|
||||||
/*
|
/*
|
||||||
Footer of each histogram. Will render range, title, and optionally
|
Footer of each histogram. Will render range, title, and optionally
|
||||||
@@ -43,7 +44,7 @@ const HistogramFooter = React.memo(
|
|||||||
data-testclass="brushable-histogram-field-name"
|
data-testclass="brushable-histogram-field-name"
|
||||||
style={{ fontStyle: "italic" }}
|
style={{ fontStyle: "italic" }}
|
||||||
>
|
>
|
||||||
{displayName}
|
{isObs ? displayName : null}
|
||||||
</span>
|
</span>
|
||||||
<div style={{ display: hideRanges ? "block" : "none" }}>
|
<div style={{ display: hideRanges ? "block" : "none" }}>
|
||||||
: {rangeMin}
|
: {rangeMin}
|
||||||
|
|||||||
@@ -75,62 +75,64 @@ const Histogram = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// BRUSH
|
if (!mini) {
|
||||||
// Note the brushable area is bounded by the data on three sides, but goes down to cover the x-axis
|
// BRUSH
|
||||||
const brushX = d3
|
// Note the brushable area is bounded by the data on three sides, but goes down to cover the x-axis
|
||||||
.brushX()
|
const brushX = d3
|
||||||
.extent([
|
.brushX()
|
||||||
[x.range()[0], y.range()[1]],
|
.extent([
|
||||||
[x.range()[1], marginTop + height + marginBottom],
|
[x.range()[0], y.range()[1]],
|
||||||
])
|
[x.range()[1], marginTop + height + marginBottom],
|
||||||
/*
|
])
|
||||||
|
/*
|
||||||
emit start so that the Undoable history can save an undo point
|
emit start so that the Undoable history can save an undo point
|
||||||
upon drag start, and ignore the subsequent intermediate drag events.
|
upon drag start, and ignore the subsequent intermediate drag events.
|
||||||
*/
|
*/
|
||||||
.on("start", onBrush(field, x.invert, "start"))
|
.on("start", onBrush(field, x.invert, "start"))
|
||||||
.on("brush", onBrush(field, x.invert, "brush"))
|
.on("brush", onBrush(field, x.invert, "brush"))
|
||||||
.on("end", onBrushEnd(field, x.invert));
|
.on("end", onBrushEnd(field, x.invert));
|
||||||
|
|
||||||
const brushXselection = container
|
const brushXselection = container
|
||||||
.insert("g")
|
.insert("g")
|
||||||
.attr("class", "brush")
|
.attr("class", "brush")
|
||||||
.attr("data-testid", `${svgRef.current.dataset.testid}-brushable-area`)
|
.attr("data-testid", `${svgRef.current.dataset.testid}-brushable-area`)
|
||||||
.call(brushX);
|
.call(brushX);
|
||||||
|
|
||||||
/* X AXIS */
|
/* X AXIS */
|
||||||
container
|
container
|
||||||
.insert("g")
|
.insert("g")
|
||||||
.attr("class", "axis axis--x")
|
.attr("class", "axis axis--x")
|
||||||
.attr("transform", `translate(0,${marginTop + height})`)
|
.attr("transform", `translate(0,${marginTop + height})`)
|
||||||
.call(
|
.call(
|
||||||
d3
|
d3
|
||||||
.axisBottom(x)
|
.axisBottom(x)
|
||||||
.ticks(4)
|
.ticks(4)
|
||||||
.tickFormat(d3.format(maybeScientific(x)))
|
.tickFormat(d3.format(maybeScientific(x)))
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Y AXIS */
|
/* Y AXIS */
|
||||||
container
|
container
|
||||||
.insert("g")
|
.insert("g")
|
||||||
.attr("class", "axis axis--y")
|
.attr("class", "axis axis--y")
|
||||||
.attr("transform", `translate(${marginLeft + width},0)`)
|
.attr("transform", `translate(${marginLeft + width},0)`)
|
||||||
.call(
|
.call(
|
||||||
d3
|
d3
|
||||||
.axisRight(y)
|
.axisRight(y)
|
||||||
.ticks(3)
|
.ticks(3)
|
||||||
.tickFormat(
|
.tickFormat(
|
||||||
d3.format(
|
d3.format(
|
||||||
y.domain().some((n) => Math.abs(n) >= 10000) ? ".0e" : ","
|
y.domain().some((n) => Math.abs(n) >= 10000) ? ".0e" : ","
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
/* axis style */
|
/* axis style */
|
||||||
svg.selectAll(".axis text").style("fill", "rgb(80,80,80)");
|
svg.selectAll(".axis text").style("fill", "rgb(80,80,80)");
|
||||||
svg.selectAll(".axis path").style("stroke", "rgb(230,230,230)");
|
svg.selectAll(".axis path").style("stroke", "rgb(230,230,230)");
|
||||||
svg.selectAll(".axis line").style("stroke", "rgb(230,230,230)");
|
svg.selectAll(".axis line").style("stroke", "rgb(230,230,230)");
|
||||||
|
|
||||||
setBrush({ brushX, brushXselection });
|
setBrush({ brushX, brushXselection });
|
||||||
|
}
|
||||||
}, [histogram, isColorBy]);
|
}, [histogram, isColorBy]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
|
||||||
import { interpolateCool } from "d3-scale-chromatic";
|
|
||||||
import * as d3 from "d3";
|
|
||||||
|
|
||||||
import maybeScientific from "./maybeScientific";
|
|
||||||
import clamp from "./clamp";
|
|
||||||
|
|
||||||
const Histogram = ({
|
|
||||||
field,
|
|
||||||
fieldForId,
|
|
||||||
display,
|
|
||||||
histogram,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
onBrush,
|
|
||||||
onBrushEnd,
|
|
||||||
margin,
|
|
||||||
isColorBy,
|
|
||||||
selectionRange,
|
|
||||||
}) => {
|
|
||||||
const svgRef = useRef(null);
|
|
||||||
const [brush, setBrush] = useState(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
/*
|
|
||||||
Create the d3 histogram
|
|
||||||
*/
|
|
||||||
const { marginLeft, marginRight, marginBottom, marginTop } = margin;
|
|
||||||
const { x, y, bins, binStart, binEnd, binWidth } = histogram;
|
|
||||||
const svg = d3.select(svgRef.current);
|
|
||||||
|
|
||||||
/* Remove everything */
|
|
||||||
svg.selectAll("*").remove();
|
|
||||||
|
|
||||||
/* Set margins within the SVG */
|
|
||||||
const container = svg
|
|
||||||
.attr("width", width + marginLeft + marginRight)
|
|
||||||
.attr("height", height + marginTop + marginBottom)
|
|
||||||
.append("g")
|
|
||||||
.attr("class", "histogram-container")
|
|
||||||
.attr("transform", `translate(${marginLeft},${marginTop})`);
|
|
||||||
|
|
||||||
const colorScale = d3
|
|
||||||
.scaleSequential(interpolateCool)
|
|
||||||
.domain([0, bins.length]);
|
|
||||||
|
|
||||||
const histogramScale = d3
|
|
||||||
.scaleLinear()
|
|
||||||
.domain(x.domain())
|
|
||||||
.range([
|
|
||||||
colorScale.domain()[1],
|
|
||||||
colorScale.domain()[0],
|
|
||||||
]); /* we flip this to make colors dark if high in the color scale */
|
|
||||||
|
|
||||||
if (binWidth > 0) {
|
|
||||||
/* BINS */
|
|
||||||
container
|
|
||||||
.insert("g", "*")
|
|
||||||
.selectAll("rect")
|
|
||||||
.data(bins)
|
|
||||||
.enter()
|
|
||||||
.append("rect")
|
|
||||||
.attr("x", (d, i) => x(binStart(i)) + 1)
|
|
||||||
.attr("y", (d) => y(d))
|
|
||||||
.attr("width", (d, i) => x(binEnd(i)) - x(binStart(i)) - 1)
|
|
||||||
.attr("height", (d) => y(0) - y(d))
|
|
||||||
.style(
|
|
||||||
"fill",
|
|
||||||
isColorBy ? (d, i) => colorScale(histogramScale(binStart(i))) : "#bbb"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// BRUSH
|
|
||||||
// Note the brushable area is bounded by the data on three sides, but goes down to cover the x-axis
|
|
||||||
const brushX = d3
|
|
||||||
.brushX()
|
|
||||||
.extent([
|
|
||||||
[x.range()[0], y.range()[1]],
|
|
||||||
[x.range()[1], marginTop + height + marginBottom],
|
|
||||||
])
|
|
||||||
/*
|
|
||||||
emit start so that the Undoable history can save an undo point
|
|
||||||
upon drag start, and ignore the subsequent intermediate drag events.
|
|
||||||
*/
|
|
||||||
.on("start", onBrush(field, x.invert, "start"))
|
|
||||||
.on("brush", onBrush(field, x.invert, "brush"))
|
|
||||||
.on("end", onBrushEnd(field, x.invert));
|
|
||||||
|
|
||||||
const brushXselection = container
|
|
||||||
.insert("g")
|
|
||||||
.attr("class", "brush")
|
|
||||||
.attr("data-testid", `${svgRef.current.dataset.testid}-brushable-area`)
|
|
||||||
.call(brushX);
|
|
||||||
|
|
||||||
/* X AXIS */
|
|
||||||
container
|
|
||||||
.insert("g")
|
|
||||||
.attr("class", "axis axis--x")
|
|
||||||
.attr("transform", `translate(0,${marginTop + height})`)
|
|
||||||
.call(
|
|
||||||
d3
|
|
||||||
.axisBottom(x)
|
|
||||||
.ticks(4)
|
|
||||||
.tickFormat(d3.format(maybeScientific(x)))
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Y AXIS */
|
|
||||||
container
|
|
||||||
.insert("g")
|
|
||||||
.attr("class", "axis axis--y")
|
|
||||||
.attr("transform", `translate(${marginLeft + width},0)`)
|
|
||||||
.call(
|
|
||||||
d3
|
|
||||||
.axisRight(y)
|
|
||||||
.ticks(3)
|
|
||||||
.tickFormat(
|
|
||||||
d3.format(
|
|
||||||
y.domain().some((n) => Math.abs(n) >= 10000) ? ".0e" : ","
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
/* axis style */
|
|
||||||
svg.selectAll(".axis text").style("fill", "rgb(80,80,80)");
|
|
||||||
svg.selectAll(".axis path").style("stroke", "rgb(230,230,230)");
|
|
||||||
svg.selectAll(".axis line").style("stroke", "rgb(230,230,230)");
|
|
||||||
|
|
||||||
setBrush({ brushX, brushXselection });
|
|
||||||
}, [histogram, isColorBy]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
/*
|
|
||||||
paint/update selection brush
|
|
||||||
*/
|
|
||||||
if (!brush) return;
|
|
||||||
const { brushX, brushXselection } = brush;
|
|
||||||
const selection = d3.brushSelection(brushXselection.node());
|
|
||||||
if (!selectionRange && selection) {
|
|
||||||
/* no active selection - clear brush */
|
|
||||||
brushXselection.call(brushX.move, null);
|
|
||||||
} else if (selectionRange) {
|
|
||||||
const { x, domain } = histogram;
|
|
||||||
const [min, max] = domain;
|
|
||||||
const x0 = x(clamp(selectionRange[0], [min, max]));
|
|
||||||
const x1 = x(clamp(selectionRange[1], [min, max]));
|
|
||||||
if (!selection) {
|
|
||||||
/* there is an active selection, but no brush - set the brush */
|
|
||||||
brushXselection.call(brushX.move, [x0, x1]);
|
|
||||||
} else {
|
|
||||||
/* there is an active selection and a brush - make sure they match */
|
|
||||||
const moveDeltaThreshold = 1;
|
|
||||||
const dX0 = Math.abs(x0 - selection[0]);
|
|
||||||
const dX1 = Math.abs(x1 - selection[1]);
|
|
||||||
/*
|
|
||||||
only update the brush if it is grossly incorrect,
|
|
||||||
as defined by the moveDeltaThreshold
|
|
||||||
*/
|
|
||||||
if (dX0 > moveDeltaThreshold || dX1 > moveDeltaThreshold) {
|
|
||||||
brushXselection.call(brushX.move, [x0, x1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [brush, selectionRange]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
style={{ display }}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
id={`histogram_${fieldForId}_svg`}
|
|
||||||
data-testclass="histogram-plot"
|
|
||||||
data-testid={`histogram-${field}-plot`}
|
|
||||||
ref={svgRef}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Histogram;
|
|
||||||
@@ -54,11 +54,6 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
const marginBottom = 25; // space for X axis & labels
|
const marginBottom = 25; // space for X axis & labels
|
||||||
const marginTop = 3;
|
const marginTop = 3;
|
||||||
|
|
||||||
const marginLeftMini = 10; // Space for 0 tick label on X axis
|
|
||||||
const marginRightMini = 54; // space for Y axis & labels
|
|
||||||
const marginBottomMini = 25; // space for X axis & labels
|
|
||||||
const marginTopMini = 3;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
margin: {
|
margin: {
|
||||||
marginLeft,
|
marginLeft,
|
||||||
@@ -69,13 +64,13 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
width: 340 - marginLeft - marginRight,
|
width: 340 - marginLeft - marginRight,
|
||||||
height: 135 - marginTop - marginBottom,
|
height: 135 - marginTop - marginBottom,
|
||||||
marginMini: {
|
marginMini: {
|
||||||
marginLeftMini,
|
marginLeft: 0, // Space for 0 tick label on X axis
|
||||||
marginRightMini,
|
marginRight: 0, // space for Y axis & labels
|
||||||
marginBottomMini,
|
marginBottom: 0, // space for X axis & labels
|
||||||
marginTopMini,
|
marginTop: 0,
|
||||||
},
|
},
|
||||||
widthMini: 340 - marginLeft - marginRight,
|
widthMini: 120,
|
||||||
heightMini: 135 - marginTop - marginBottom,
|
heightMini: 15,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,16 +266,20 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this -- instance method allows for memoization per annotation
|
// eslint-disable-next-line class-methods-use-this -- instance method allows for memoization per annotation
|
||||||
calcHistogramCache(col, margin, width, height) {
|
calcHistogramCache(col, margin, width, height) {
|
||||||
|
/* make this more structured and doing a forEach */
|
||||||
/*
|
/*
|
||||||
recalculate expensive stuff, notably bins, summaries, etc.
|
recalculate expensive stuff, notably bins, summaries, etc.
|
||||||
*/
|
*/
|
||||||
const histogramCache = {};
|
const histogramCache = {}; /* maybe change this so that it computes ... */
|
||||||
const summary = col.summarize();
|
const summary = col.summarize(); /* this is memoized, so it's free the second time you call it */
|
||||||
const { min: domainMin, max: domainMax } = summary;
|
const { min: domainMin, max: domainMax } = summary;
|
||||||
const numBins = 40;
|
const numBins = 40;
|
||||||
const { marginTop, marginLeft } = margin;
|
const { marginTop, marginLeft } = margin; /* changes with mini */
|
||||||
|
|
||||||
histogramCache.domain = [domainMin, domainMax];
|
histogramCache.domain = [
|
||||||
|
domainMin,
|
||||||
|
domainMax,
|
||||||
|
]; /* doesn't change with mini */
|
||||||
|
|
||||||
histogramCache.x = d3
|
histogramCache.x = d3
|
||||||
.scaleLinear()
|
.scaleLinear()
|
||||||
@@ -290,7 +289,7 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
histogramCache.bins = histogramContinuous(col, numBins, [
|
histogramCache.bins = histogramContinuous(col, numBins, [
|
||||||
domainMin,
|
domainMin,
|
||||||
domainMax,
|
domainMax,
|
||||||
]);
|
]); /* memoized */
|
||||||
histogramCache.binWidth = (domainMax - domainMin) / numBins;
|
histogramCache.binWidth = (domainMax - domainMin) / numBins;
|
||||||
|
|
||||||
histogramCache.binStart = (i) => domainMin + i * histogramCache.binWidth;
|
histogramCache.binStart = (i) => domainMin + i * histogramCache.binWidth;
|
||||||
@@ -377,11 +376,11 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
: "histogram-continuous-metadata"
|
: "histogram-continuous-metadata"
|
||||||
}
|
}
|
||||||
style={{
|
style={{
|
||||||
padding: globals.leftSidebarSectionPadding,
|
padding: mini ? 0 : globals.leftSidebarSectionPadding,
|
||||||
backgroundColor: zebra ? globals.lightestGrey : "white",
|
backgroundColor: zebra ? globals.lightestGrey : "white",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!mini ? (
|
{!mini && isObs ? (
|
||||||
<HistogramHeader
|
<HistogramHeader
|
||||||
fieldId={field}
|
fieldId={field}
|
||||||
isColorBy={isColorAccessor}
|
isColorBy={isColorAccessor}
|
||||||
@@ -416,6 +415,7 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
/>
|
/>
|
||||||
{!mini ? (
|
{!mini ? (
|
||||||
<HistogramFooter
|
<HistogramFooter
|
||||||
|
isObs={isObs}
|
||||||
displayName={field}
|
displayName={field}
|
||||||
hideRanges={asyncProps.isSingleValue}
|
hideRanges={asyncProps.isSingleValue}
|
||||||
rangeMin={asyncProps.unclippedRange[0]}
|
rangeMin={asyncProps.unclippedRange[0]}
|
||||||
|
|||||||
@@ -11,10 +11,18 @@ import HistogramBrush from "../brushableHistogram";
|
|||||||
|
|
||||||
// import TestMiniHisto from "./test_miniHisto";
|
// import TestMiniHisto from "./test_miniHisto";
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
|
import actions from "../../actions";
|
||||||
|
|
||||||
// import GeneMenus from "./menus/geneMenus";
|
// import GeneMenus from "./menus/geneMenus";
|
||||||
|
|
||||||
@connect(() => {
|
@connect((state, ownProps) => {
|
||||||
return {};
|
const { gene } = ownProps;
|
||||||
|
|
||||||
|
return {
|
||||||
|
isColorAccessor: state.colors.colorAccessor === gene,
|
||||||
|
isScatterplotXXaccessor: state.controls.scatterplotXXaccessor === gene,
|
||||||
|
isScatterplotYYaccessor: state.controls.scatterplotYYaccessor === gene,
|
||||||
|
};
|
||||||
})
|
})
|
||||||
class Gene extends React.Component {
|
class Gene extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -26,10 +34,7 @@ class Gene extends React.Component {
|
|||||||
|
|
||||||
onColorChangeClick = () => {
|
onColorChangeClick = () => {
|
||||||
const { dispatch, gene } = this.props;
|
const { dispatch, gene } = this.props;
|
||||||
dispatch({
|
dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(gene));
|
||||||
type: "color by expression",
|
|
||||||
colorAccessor: gene,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleGeneExpandClick = () => {
|
handleGeneExpandClick = () => {
|
||||||
@@ -37,26 +42,42 @@ class Gene extends React.Component {
|
|||||||
this.setState({ geneIsExpanded: !geneIsExpanded });
|
this.setState({ geneIsExpanded: !geneIsExpanded });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleSetGeneAsScatterplotX = () => {
|
||||||
|
const { dispatch, gene } = this.props;
|
||||||
|
dispatch({
|
||||||
|
type: "set scatterplot x",
|
||||||
|
data: gene,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleSetGeneAsScatterplotY = () => {
|
||||||
|
const { dispatch, gene } = this.props;
|
||||||
|
dispatch({
|
||||||
|
type: "set scatterplot y",
|
||||||
|
data: gene,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { gene } = this.props;
|
const {
|
||||||
|
gene,
|
||||||
|
isColorAccessor,
|
||||||
|
isScatterplotXXaccessor,
|
||||||
|
isScatterplotYYaccessor,
|
||||||
|
} = this.props;
|
||||||
const { geneIsExpanded } = this.state;
|
const { geneIsExpanded } = this.state;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginLeft: 15,
|
marginLeft: 15,
|
||||||
|
marginRight: 0,
|
||||||
|
marginTop: 2,
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon
|
|
||||||
icon="drag-handle-horizontal"
|
|
||||||
iconSize={12}
|
|
||||||
style={{
|
|
||||||
marginRight: 10,
|
|
||||||
cursor: "grab",
|
|
||||||
position: "relative",
|
|
||||||
top: -2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
tabIndex="0"
|
tabIndex="0"
|
||||||
@@ -65,8 +86,20 @@ class Gene extends React.Component {
|
|||||||
onKeyPress={/* todo_genesets */ () => {}}
|
onKeyPress={/* todo_genesets */ () => {}}
|
||||||
style={{
|
style={{
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Icon
|
||||||
|
icon="drag-handle-horizontal"
|
||||||
|
iconSize={12}
|
||||||
|
style={{
|
||||||
|
marginRight: 7,
|
||||||
|
cursor: "grab",
|
||||||
|
position: "relative",
|
||||||
|
top: 3,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Truncate>
|
<Truncate>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
@@ -79,22 +112,58 @@ class Gene extends React.Component {
|
|||||||
{gene}
|
{gene}
|
||||||
</span>
|
</span>
|
||||||
</Truncate>
|
</Truncate>
|
||||||
|
{!geneIsExpanded ? (
|
||||||
|
<HistogramBrush isUserDefined field={gene} mini />
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<AnchorButton
|
||||||
|
minimal
|
||||||
|
small
|
||||||
|
data-testid={`plot-x-${gene}`}
|
||||||
|
onClick={this.handleSetGeneAsScatterplotX}
|
||||||
|
active={isScatterplotXXaccessor}
|
||||||
|
intent={isScatterplotXXaccessor ? "primary" : "none"}
|
||||||
|
style={{ fontWeight: 700, marginRight: 2 }}
|
||||||
|
>
|
||||||
|
x
|
||||||
|
</AnchorButton>
|
||||||
|
<AnchorButton
|
||||||
|
minimal
|
||||||
|
small
|
||||||
|
data-testid={`plot-y-${gene}`}
|
||||||
|
onClick={this.handleSetGeneAsScatterplotY}
|
||||||
|
active={isScatterplotYYaccessor}
|
||||||
|
intent={isScatterplotYYaccessor ? "primary" : "none"}
|
||||||
|
style={{ fontWeight: 700, marginRight: 2 }}
|
||||||
|
>
|
||||||
|
y
|
||||||
|
</AnchorButton>
|
||||||
<AnchorButton
|
<AnchorButton
|
||||||
minimal
|
minimal
|
||||||
small
|
small
|
||||||
data-testclass="maximize"
|
data-testclass="maximize"
|
||||||
data-testid={`maximize-${gene}`}
|
data-testid={`maximize-${gene}`}
|
||||||
onClick={this.handleGeneExpandClick}
|
onClick={this.handleGeneExpandClick}
|
||||||
active={false /* todo gene sets */}
|
active={geneIsExpanded}
|
||||||
intent="none"
|
intent="none"
|
||||||
icon={<Icon icon="maximize" iconSize={10} />}
|
icon={<Icon icon="maximize" iconSize={10} />}
|
||||||
|
style={{ marginRight: 2 }}
|
||||||
|
/>
|
||||||
|
<AnchorButton
|
||||||
|
minimal
|
||||||
|
small
|
||||||
|
data-testclass="colorby"
|
||||||
|
data-testid={`colorby-${gene}`}
|
||||||
|
onClick={this.onColorChangeClick}
|
||||||
|
active={isColorAccessor}
|
||||||
|
intent={isColorAccessor ? "primary" : "none"}
|
||||||
|
icon={<Icon icon="tint" iconSize={12} />}
|
||||||
/>
|
/>
|
||||||
{/* <TestMiniHisto /> */}
|
|
||||||
</span>
|
</span>
|
||||||
|
{/* <TestMiniHisto /> */}
|
||||||
</div>
|
</div>
|
||||||
|
{geneIsExpanded ? <HistogramBrush isUserDefined field={gene} /> : null}
|
||||||
<HistogramBrush isUserDefined field={gene} mini={!geneIsExpanded} />
|
|
||||||
|
|
||||||
{/* <GeneMenus genesetsEditable gene={gene} /> */}
|
{/* <GeneMenus genesetsEditable gene={gene} /> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -103,17 +172,6 @@ class Gene extends React.Component {
|
|||||||
|
|
||||||
export default Gene;
|
export default Gene;
|
||||||
|
|
||||||
// <AnchorButton
|
|
||||||
// minimal
|
|
||||||
// small
|
|
||||||
// data-testclass="colorby"
|
|
||||||
// data-testid={`colorby-${gene}`}
|
|
||||||
// onClick={/* todo gene sets */ () => {}}
|
|
||||||
// active={false /* todo gene sets */}
|
|
||||||
// intent="none"
|
|
||||||
// icon={<Icon icon="tint" iconSize={12} />}
|
|
||||||
// />
|
|
||||||
|
|
||||||
// {geneIsExpanded ? (
|
// {geneIsExpanded ? (
|
||||||
// <FaChevronDown
|
// <FaChevronDown
|
||||||
// data-testclass="gene-expand-is-expanded"
|
// data-testclass="gene-expand-is-expanded"
|
||||||
|
|||||||
Reference in New Issue
Block a user