mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-10 08:10:56 +08:00
Add margins to histogram SVGs (#1085)
* Add a left margin to histogram SVGs Fixes https://github.com/chanzuckerberg/cellxgene/issues/1042 1. Added "margins" to the SVG as in https://bl.ocks.org/mbostock/3019563 2. Tailored the brushable area to be just where the data is and locked puppeteerUtils to use that in e2e testing. * Dial in margins to avoid y-axis clipping
This commit is contained in:
@@ -41,7 +41,7 @@ export const datasets = {
|
|||||||
{
|
{
|
||||||
metadata: "n_genes",
|
metadata: "n_genes",
|
||||||
"coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 },
|
"coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 },
|
||||||
count: "1537"
|
count: "1552"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -126,12 +126,12 @@ describe("cell selection", () => {
|
|||||||
|
|
||||||
test("selects cells via continuous", async () => {
|
test("selects cells via continuous", async () => {
|
||||||
for (const cellset of data.cellsets.continuous) {
|
for (const cellset of data.cellsets.continuous) {
|
||||||
const histId = `histogram-${cellset.metadata}-plot-brush`;
|
const histBrushableAreaId = `histogram-${cellset.metadata}-plot-brushable-area`;
|
||||||
const coords = await cxgActions.calcDragCoordinates(
|
const coords = await cxgActions.calcDragCoordinates(
|
||||||
histId,
|
histBrushableAreaId,
|
||||||
cellset["coordinates-as-percent"]
|
cellset["coordinates-as-percent"]
|
||||||
);
|
);
|
||||||
await cxgActions.drag(histId, coords.start, coords.end);
|
await cxgActions.drag(histBrushableAreaId, coords.start, coords.end);
|
||||||
const cellCount = await cxgActions.cellSet(1);
|
const cellCount = await cxgActions.cellSet(1);
|
||||||
expect(cellCount).toBe(cellset.count);
|
expect(cellCount).toBe(cellset.count);
|
||||||
}
|
}
|
||||||
@@ -280,12 +280,12 @@ describe("scatter plot", () => {
|
|||||||
describe("clipping", () => {
|
describe("clipping", () => {
|
||||||
test("clip continuous", async () => {
|
test("clip continuous", async () => {
|
||||||
await cxgActions.clip(data.clip.min, data.clip.max);
|
await cxgActions.clip(data.clip.min, data.clip.max);
|
||||||
const histId = `histogram-${data.clip.metadata}-plot-brush`;
|
const histBrushableAreaId = `histogram-${data.clip.metadata}-plot-brushable-area`;
|
||||||
const coords = await cxgActions.calcDragCoordinates(
|
const coords = await cxgActions.calcDragCoordinates(
|
||||||
histId,
|
histBrushableAreaId,
|
||||||
data.clip["coordinates-as-percent"]
|
data.clip["coordinates-as-percent"]
|
||||||
);
|
);
|
||||||
await cxgActions.drag(histId, coords.start, coords.end);
|
await cxgActions.drag(histBrushableAreaId, coords.start, coords.end);
|
||||||
const cellCount = await cxgActions.cellSet(1);
|
const cellCount = await cxgActions.cellSet(1);
|
||||||
expect(cellCount).toBe(data.clip.count);
|
expect(cellCount).toBe(data.clip.count);
|
||||||
});
|
});
|
||||||
@@ -295,12 +295,12 @@ describe("clipping", () => {
|
|||||||
await page.keyboard.press("Enter");
|
await page.keyboard.press("Enter");
|
||||||
await page.waitForSelector(`[data-testid='histogram-${data.clip.gene}']`);
|
await page.waitForSelector(`[data-testid='histogram-${data.clip.gene}']`);
|
||||||
await cxgActions.clip(data.clip.min, data.clip.max);
|
await cxgActions.clip(data.clip.min, data.clip.max);
|
||||||
const histId = `histogram-${data.clip.gene}-plot-brush`;
|
const histBrushableAreaId = `histogram-${data.clip.gene}-plot-brushable-area`;
|
||||||
const coords = await cxgActions.calcDragCoordinates(
|
const coords = await cxgActions.calcDragCoordinates(
|
||||||
histId,
|
histBrushableAreaId,
|
||||||
data.clip["coordinates-as-percent"]
|
data.clip["coordinates-as-percent"]
|
||||||
);
|
);
|
||||||
await cxgActions.drag(histId, coords.start, coords.end);
|
await cxgActions.drag(histBrushableAreaId, coords.start, coords.end);
|
||||||
const cellCount = await cxgActions.cellSet(1);
|
const cellCount = await cxgActions.cellSet(1);
|
||||||
expect(cellCount).toBe(data.clip["gene-cell-count"]);
|
expect(cellCount).toBe(data.clip["gene-cell-count"]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
https://bl.ocks.org/mbostock/4341954
|
https://bl.ocks.org/mbostock/4341954
|
||||||
https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
|
https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
|
||||||
https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771
|
https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771
|
||||||
|
https://bl.ocks.org/mbostock/3019563
|
||||||
*/
|
*/
|
||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@@ -55,7 +56,7 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
histogramCache.x = d3
|
histogramCache.x = d3
|
||||||
.scaleLinear()
|
.scaleLinear()
|
||||||
.domain([domainMin, domainMax])
|
.domain([domainMin, domainMax])
|
||||||
.range([0, this.width - this.marginRight]);
|
.range([this.marginLeft, this.marginLeft + this.width]);
|
||||||
|
|
||||||
histogramCache.bins = d3
|
histogramCache.bins = d3
|
||||||
.histogram()
|
.histogram()
|
||||||
@@ -68,7 +69,7 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
histogramCache.y = d3
|
histogramCache.y = d3
|
||||||
.scaleLinear()
|
.scaleLinear()
|
||||||
.domain([0, yMax])
|
.domain([0, yMax])
|
||||||
.range([this.height - this.marginBottom, 0]);
|
.range([this.marginTop + this.height, this.marginTop]);
|
||||||
|
|
||||||
return histogramCache;
|
return histogramCache;
|
||||||
});
|
});
|
||||||
@@ -76,10 +77,13 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.width = 340;
|
this.marginLeft = 3; // Space for 0 tick label on X axis
|
||||||
this.height = 100;
|
|
||||||
this.marginBottom = 20; // space for X axis & labels
|
|
||||||
this.marginRight = 40; // space for Y axis & labels
|
this.marginRight = 40; // space for Y axis & labels
|
||||||
|
this.marginBottom = 25; // space for X axis & labels
|
||||||
|
this.marginTop = 3;
|
||||||
|
|
||||||
|
this.width = 340 - this.marginLeft - this.marginRight;
|
||||||
|
this.height = 135 - this.marginTop - this.marginBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -315,8 +319,16 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
/* Remove everything */
|
/* Remove everything */
|
||||||
svg.selectAll("*").remove();
|
svg.selectAll("*").remove();
|
||||||
|
|
||||||
|
/* Set margins within the SVG */
|
||||||
|
const container = svg
|
||||||
|
.attr("width", this.width + this.marginLeft + this.marginRight)
|
||||||
|
.attr("height", this.height + this.marginTop + this.marginBottom)
|
||||||
|
.append("g")
|
||||||
|
.attr("class", "histogram-container")
|
||||||
|
.attr("transform", `translate(${this.marginLeft},${this.marginTop})`);
|
||||||
|
|
||||||
/* BINS */
|
/* BINS */
|
||||||
svg
|
container
|
||||||
.insert("g", "*")
|
.insert("g", "*")
|
||||||
.attr("fill", "#bbb")
|
.attr("fill", "#bbb")
|
||||||
.selectAll("rect")
|
.selectAll("rect")
|
||||||
@@ -328,10 +340,14 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
.attr("width", d => Math.abs(x(d.x1) - x(d.x0) - 1))
|
.attr("width", d => Math.abs(x(d.x1) - x(d.x0) - 1))
|
||||||
.attr("height", d => y(0) - y(d.length));
|
.attr("height", d => y(0) - y(d.length));
|
||||||
|
|
||||||
/* BRUSH */
|
// BRUSH
|
||||||
|
// Note the brushable area is bounded by the data on three sides, but goes down to cover the x-axis
|
||||||
const brushX = d3
|
const brushX = d3
|
||||||
.brushX()
|
.brushX()
|
||||||
.extent([[0, 0], [this.width - this.marginRight, this.height]])
|
.extent([
|
||||||
|
[x.range()[0], y.range()[1]],
|
||||||
|
[x.range()[1], this.marginTop + this.height + this.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.
|
||||||
@@ -339,18 +355,18 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
.on("start", this.onBrush(field, x.invert, "start").bind(this))
|
.on("start", this.onBrush(field, x.invert, "start").bind(this))
|
||||||
.on("brush", this.onBrush(field, x.invert, "brush").bind(this))
|
.on("brush", this.onBrush(field, x.invert, "brush").bind(this))
|
||||||
.on("end", this.onBrushEnd(field, x.invert).bind(this));
|
.on("end", this.onBrushEnd(field, x.invert).bind(this));
|
||||||
const brushXselection = d3
|
|
||||||
.select(svgRef)
|
const brushXselection = container
|
||||||
.append("g")
|
.insert("g")
|
||||||
.attr("class", "brush")
|
.attr("class", "brush")
|
||||||
.attr("data-testid", `${svgRef.dataset.testid}-brush`)
|
.attr("data-testid", `${svgRef.dataset.testid}-brushable-area`)
|
||||||
.call(brushX);
|
.call(brushX);
|
||||||
|
|
||||||
/* X AXIS */
|
/* X AXIS */
|
||||||
svg
|
container
|
||||||
.append("g")
|
.insert("g")
|
||||||
.attr("class", "axis axis--x")
|
.attr("class", "axis axis--x")
|
||||||
.attr("transform", `translate(0,${this.height - this.marginBottom})`)
|
.attr("transform", `translate(0,${this.marginTop + this.height})`)
|
||||||
.call(
|
.call(
|
||||||
d3
|
d3
|
||||||
.axisBottom(x)
|
.axisBottom(x)
|
||||||
@@ -359,10 +375,10 @@ class HistogramBrush extends React.PureComponent {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/* Y AXIS */
|
/* Y AXIS */
|
||||||
svg
|
container
|
||||||
.append("g")
|
.insert("g")
|
||||||
.attr("class", "axis axis--y")
|
.attr("class", "axis axis--y")
|
||||||
.attr("transform", `translate(${this.width - this.marginRight},0)`)
|
.attr("transform", `translate(${this.marginLeft + this.width},0)`)
|
||||||
.call(
|
.call(
|
||||||
d3
|
d3
|
||||||
.axisRight(y)
|
.axisRight(y)
|
||||||
|
|||||||
Reference in New Issue
Block a user