Skip to content

Commit c4a3795

Browse files
authored
fix: Ordering of subplots (#1111)
- User is expecting the plots to be ordered 0 at top, increasing downward - Plotlys bounds coordinate system has 0,0 in the bottom left, increasing upward - Invert the coordinates so that it appears in plotly correctly - Add unit tests
1 parent c06d45a commit c4a3795

3 files changed

Lines changed: 122 additions & 6 deletions

File tree

packages/chart/src/ChartTestUtils.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,41 @@ class ChartTestUtils {
8383
series = [ChartTestUtils.makeSeries()],
8484
axes = ChartTestUtils.makeDefaultAxes(),
8585
showLegend = null,
86+
rowspan = 1,
87+
colspan = 1,
88+
row = 0,
89+
column = 0,
8690
}: {
8791
title?: string;
8892
series?: Series[];
8993
axes?: Axis[];
9094
showLegend?: boolean | null;
95+
rowspan?: number;
96+
colspan?: number;
97+
row?: number;
98+
column?: number;
9199
} = {}): Chart {
92100
// eslint-disable-next-line @typescript-eslint/no-explicit-any
93-
return new (dh as any).Chart({ title, series, axes, showLegend });
101+
return new (dh as any).Chart({
102+
title,
103+
series,
104+
axes,
105+
showLegend,
106+
row,
107+
column,
108+
rowspan,
109+
colspan,
110+
});
94111
}
95112

96113
static makeFigure({
97114
title = 'Figure',
98115
charts = [ChartTestUtils.makeChart()],
116+
rows = 1,
117+
cols = 1,
99118
} = {}): Figure {
100119
// eslint-disable-next-line @typescript-eslint/no-explicit-any
101-
return new (dh as any).plot.Figure({ title, charts });
120+
return new (dh as any).plot.Figure({ title, charts, rows, cols });
102121
}
103122
}
104123

packages/chart/src/ChartUtils.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,101 @@ describe('updating layout axes', () => {
315315
});
316316
});
317317

318+
describe('handles subplots and columns/rows correctly', () => {
319+
const width = ChartUtils.AXIS_SIZE_PX * 5;
320+
const height = ChartUtils.AXIS_SIZE_PX * 10;
321+
const halfXMargin = ChartUtils.AXIS_SIZE_PX / width / 2;
322+
const halfYMargin = ChartUtils.AXIS_SIZE_PX / height / 2;
323+
324+
it('handles row location correctly', () => {
325+
const axes = ChartTestUtils.makeDefaultAxes();
326+
const charts = [
327+
ChartTestUtils.makeChart({ axes, row: 0 }),
328+
ChartTestUtils.makeChart({ axes, row: 1 }),
329+
];
330+
const figure = ChartTestUtils.makeFigure({ charts, rows: 2 });
331+
expect(
332+
ChartUtils.getChartBounds(figure, charts[0], width, height)
333+
).toEqual({ bottom: 0.5 + halfYMargin, top: 1, left: 0, right: 1 });
334+
expect(
335+
ChartUtils.getChartBounds(figure, charts[1], width, height)
336+
).toEqual({ bottom: 0, top: 0.5 - halfYMargin, left: 0, right: 1 });
337+
});
338+
339+
it('handles column location correctly', () => {
340+
const axes = ChartTestUtils.makeDefaultAxes();
341+
const charts = [
342+
ChartTestUtils.makeChart({ axes, column: 0 }),
343+
ChartTestUtils.makeChart({ axes, column: 1 }),
344+
];
345+
const figure = ChartTestUtils.makeFigure({ charts, cols: 2 });
346+
expect(
347+
ChartUtils.getChartBounds(figure, charts[0], width, height)
348+
).toEqual({ bottom: 0, top: 1, left: 0, right: 0.5 - halfXMargin });
349+
expect(
350+
ChartUtils.getChartBounds(figure, charts[1], width, height)
351+
).toEqual({ bottom: 0, top: 1, left: 0.5 + halfXMargin, right: 1 });
352+
});
353+
354+
it('handles colspan', () => {
355+
const axes = ChartTestUtils.makeDefaultAxes();
356+
const charts = [
357+
ChartTestUtils.makeChart({ axes, column: 0 }),
358+
ChartTestUtils.makeChart({ axes, column: 1 }),
359+
ChartTestUtils.makeChart({ axes, row: 1, colspan: 2 }),
360+
];
361+
const figure = ChartTestUtils.makeFigure({ charts, cols: 2, rows: 2 });
362+
expect(ChartUtils.getChartBounds(figure, charts[0], width, height)).toEqual(
363+
{
364+
bottom: 0.5 + halfYMargin,
365+
top: 1,
366+
left: 0,
367+
right: 0.5 - halfXMargin,
368+
}
369+
);
370+
expect(ChartUtils.getChartBounds(figure, charts[1], width, height)).toEqual(
371+
{
372+
bottom: 0.5 + halfYMargin,
373+
top: 1,
374+
left: 0.5 + halfXMargin,
375+
right: 1,
376+
}
377+
);
378+
expect(
379+
ChartUtils.getChartBounds(figure, charts[2], width, height)
380+
).toEqual({ bottom: 0, top: 0.5 - halfYMargin, left: 0, right: 1 });
381+
});
382+
383+
it('handles rowspan', () => {
384+
const axes = ChartTestUtils.makeDefaultAxes();
385+
const charts = [
386+
ChartTestUtils.makeChart({ axes, row: 0 }),
387+
ChartTestUtils.makeChart({ axes, row: 1 }),
388+
ChartTestUtils.makeChart({ axes, column: 1, rowspan: 2 }),
389+
];
390+
const figure = ChartTestUtils.makeFigure({ charts, cols: 2, rows: 2 });
391+
expect(ChartUtils.getChartBounds(figure, charts[0], width, height)).toEqual(
392+
{
393+
bottom: 0.5 + halfYMargin,
394+
top: 1,
395+
left: 0,
396+
right: 0.5 - halfXMargin,
397+
}
398+
);
399+
expect(ChartUtils.getChartBounds(figure, charts[1], width, height)).toEqual(
400+
{
401+
bottom: 0,
402+
top: 0.5 - halfYMargin,
403+
left: 0,
404+
right: 0.5 - halfXMargin,
405+
}
406+
);
407+
expect(
408+
ChartUtils.getChartBounds(figure, charts[2], width, height)
409+
).toEqual({ bottom: 0, top: 1, left: 0.5 + halfXMargin, right: 1 });
410+
});
411+
});
412+
318413
describe('returns the axis layout ranges properly', () => {
319414
function makeLayout(layout) {
320415
return {

packages/chart/src/ChartUtils.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,8 +1140,8 @@ class ChartUtils {
11401140
static getChartBounds(
11411141
figure: Figure,
11421142
chart: Chart,
1143-
plotWidth = 0,
1144-
plotHeight = 0
1143+
plotWidth: number,
1144+
plotHeight: number
11451145
): ChartBounds {
11461146
const { cols, rows } = figure;
11471147
const { column, colspan, row, rowspan } = chart;
@@ -1154,9 +1154,11 @@ class ChartUtils {
11541154
const yMarginSize = ChartUtils.AXIS_SIZE_PX / plotHeight;
11551155

11561156
const bounds: ChartBounds = {
1157+
// Need to invert the row positioning so the first one defined shows up on top instead of the bottom, since coordinates start in bottom left
1158+
bottom: (rows - endRow) * rowSize + (endRow < rows ? yMarginSize / 2 : 0),
1159+
top: (rows - row) * rowSize - (row > 0 ? yMarginSize / 2 : 0),
1160+
11571161
left: column * columnSize + (column > 0 ? xMarginSize / 2 : 0),
1158-
bottom: row * rowSize + (row > 0 ? yMarginSize / 2 : 0),
1159-
top: endRow * rowSize - (endRow < rows ? yMarginSize / 2 : 0),
11601162
right: endColumn * columnSize - (endColumn < cols ? xMarginSize / 2 : 0),
11611163
};
11621164

0 commit comments

Comments
 (0)