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:
Matt Weiden
2020-01-06 10:46:06 -08:00
committed by GitHub
parent 327d005a1f
commit b172ff7f15
3 changed files with 44 additions and 28 deletions
+1 -1
View File
@@ -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"
} }
] ]
}, },
+9 -9
View File
@@ -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)