From 5751ce079f5535520f7424db812dddfa8200d4e5 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Fri, 16 Aug 2019 08:23:44 +0200 Subject: [PATCH] fix(axis): limit chart dimensions to avoid axis labels overflow (#314) --- src/chart_types/xy_chart/store/chart_state.ts | 7 +- .../__snapshots__/dimensions.test.ts.snap | 16 +-- .../xy_chart/utils/axis_utils.test.ts | 101 +++++++++----- src/chart_types/xy_chart/utils/axis_utils.ts | 71 +++++----- .../xy_chart/utils/dimensions.test.ts | 21 +-- src/chart_types/xy_chart/utils/dimensions.ts | 76 ++++++----- src/components/react_canvas/axis.tsx | 127 ++++++++++-------- stories/axis.tsx | 4 + 8 files changed, 232 insertions(+), 191 deletions(-) diff --git a/src/chart_types/xy_chart/store/chart_state.ts b/src/chart_types/xy_chart/store/chart_state.ts index 61defbd6e9..46653097ea 100644 --- a/src/chart_types/xy_chart/store/chart_state.ts +++ b/src/chart_types/xy_chart/store/chart_state.ts @@ -913,8 +913,8 @@ export class ChartStore { }); bboxCalculator.destroy(); - // compute chart dimensions - this.chartDimensions = computeChartDimensions( + // // compute chart dimensions + const computedChartDims = computeChartDimensions( this.parentDimensions, this.chartTheme, this.axesTicksDimensions, @@ -922,6 +922,7 @@ export class ChartStore { this.showLegend.get() && !this.legendCollapsed.get(), this.legendPosition, ); + this.chartDimensions = computedChartDims.chartDimensions; this.chartTransform = computeChartTransform(this.chartDimensions, this.chartRotation); this.brushExtent = computeBrushExtent(this.chartDimensions, this.chartRotation, this.chartTransform); @@ -954,7 +955,7 @@ export class ChartStore { // compute visible ticks and their positions const axisTicksPositions = getAxisTicksPositions( - this.chartDimensions, + computedChartDims, this.chartTheme, this.chartRotation, this.showLegend.get() && !this.legendCollapsed.get(), diff --git a/src/chart_types/xy_chart/utils/__snapshots__/dimensions.test.ts.snap b/src/chart_types/xy_chart/utils/__snapshots__/dimensions.test.ts.snap index a4ba19bd47..83ceb016c1 100644 --- a/src/chart_types/xy_chart/utils/__snapshots__/dimensions.test.ts.snap +++ b/src/chart_types/xy_chart/utils/__snapshots__/dimensions.test.ts.snap @@ -12,26 +12,26 @@ Object { exports[`Computed chart dimensions should be padded by a bottom axis 1`] = ` Object { "height": 30, - "left": 20, + "left": 25, "top": 20, - "width": 60, + "width": 50, } `; exports[`Computed chart dimensions should be padded by a left axis 1`] = ` Object { - "height": 60, + "height": 50, "left": 50, - "top": 20, + "top": 25, "width": 30, } `; exports[`Computed chart dimensions should be padded by a right axis 1`] = ` Object { - "height": 60, + "height": 50, "left": 20, - "top": 20, + "top": 25, "width": 30, } `; @@ -39,8 +39,8 @@ Object { exports[`Computed chart dimensions should be padded by a top axis 1`] = ` Object { "height": 30, - "left": 20, + "left": 25, "top": 50, - "width": 60, + "width": 50, } `; diff --git a/src/chart_types/xy_chart/utils/axis_utils.test.ts b/src/chart_types/xy_chart/utils/axis_utils.test.ts index b90217232a..913f7102a5 100644 --- a/src/chart_types/xy_chart/utils/axis_utils.test.ts +++ b/src/chart_types/xy_chart/utils/axis_utils.test.ts @@ -565,18 +565,24 @@ describe('Axis computational utils', () => { const tickSize = 10; const tickPadding = 5; const tickPosition = 0; - + const axisPosition = { + top: 0, + left: 0, + width: 100, + height: 100, + }; const unrotatedLabelProps = getTickLabelProps( tickLabelRotation, tickSize, tickPadding, tickPosition, Position.Left, + axisPosition, axis1Dims, ); expect(unrotatedLabelProps).toEqual({ - x: -10, + x: 75, y: -5, align: 'right', verticalAlign: 'middle', @@ -589,11 +595,12 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Left, + axisPosition, axis1Dims, ); expect(rotatedLabelProps).toEqual({ - x: -10, + x: 75, y: -5, align: 'center', verticalAlign: 'middle', @@ -605,6 +612,7 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Right, + axisPosition, axis1Dims, ); @@ -622,6 +630,7 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Right, + axisPosition, axis1Dims, ); @@ -638,6 +647,12 @@ describe('Axis computational utils', () => { const tickSize = 10; const tickPadding = 5; const tickPosition = 0; + const axisPosition = { + top: 0, + left: 0, + width: 100, + height: 100, + }; const unrotatedLabelProps = getTickLabelProps( tickLabelRotation, @@ -645,12 +660,13 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Top, + axisPosition, axis1Dims, ); expect(unrotatedLabelProps).toEqual({ x: -5, - y: 0, + y: 75, align: 'center', verticalAlign: 'bottom', }); @@ -662,12 +678,13 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Top, + axisPosition, axis1Dims, ); expect(rotatedLabelProps).toEqual({ x: -5, - y: 0, + y: 75, align: 'center', verticalAlign: 'middle', }); @@ -678,6 +695,7 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Bottom, + axisPosition, axis1Dims, ); @@ -695,6 +713,7 @@ describe('Axis computational utils', () => { tickPadding, tickPosition, Position.Bottom, + axisPosition, axis1Dims, ); @@ -710,11 +729,11 @@ describe('Axis computational utils', () => { const tickPadding = 5; const tickSize = 10; const tickPosition = 10; - const maxLabelBboxHeight = 20; + const axisHeight = 20; const leftAxisTickLinePositions = getVerticalAxisTickLineProps(Position.Left, tickPadding, tickSize, tickPosition); - expect(leftAxisTickLinePositions).toEqual([5, 10, 15, 10]); + expect(leftAxisTickLinePositions).toEqual([5, 10, -5, 10]); const rightAxisTickLinePositions = getVerticalAxisTickLineProps( Position.Right, @@ -725,22 +744,15 @@ describe('Axis computational utils', () => { expect(rightAxisTickLinePositions).toEqual([0, 10, 10, 10]); - const topAxisTickLinePositions = getHorizontalAxisTickLineProps( - Position.Top, - tickPadding, - tickSize, - tickPosition, - maxLabelBboxHeight, - ); + const topAxisTickLinePositions = getHorizontalAxisTickLineProps(Position.Top, axisHeight, tickSize, tickPosition); - expect(topAxisTickLinePositions).toEqual([10, 25, 10, 35]); + expect(topAxisTickLinePositions).toEqual([10, 10, 10, 20]); const bottomAxisTickLinePositions = getHorizontalAxisTickLineProps( Position.Bottom, - tickPadding, + axisHeight, tickSize, tickPosition, - maxLabelBboxHeight, ); expect(bottomAxisTickLinePositions).toEqual([10, 0, 10, 10]); @@ -774,7 +786,10 @@ describe('Axis computational utils', () => { axisDims.set(verticalAxisSpecWTitle.id, axis1Dims); let axisTicksPosition = getAxisTicksPositions( - chartDim, + { + chartDimensions: chartDim, + leftMargin: 0, + }, LIGHT_THEME, chartRotation, showLegend, @@ -786,11 +801,10 @@ describe('Axis computational utils', () => { false, ); - let left = 12 + 5 + 10 + 10; // font size + title padding + chart margin left + label width expect(axisTicksPosition.axisPositions.get(verticalAxisSpecWTitle.id)).toEqual({ top: 0, - left, - width: 10, + left: 10, + width: 12 + 5 + 10 + 10 + 10, height: 100, }); @@ -799,7 +813,10 @@ describe('Axis computational utils', () => { axisDims.set(verticalAxisSpec.id, axis1Dims); axisTicksPosition = getAxisTicksPositions( - chartDim, + { + chartDimensions: chartDim, + leftMargin: 0, + }, LIGHT_THEME, chartRotation, showLegend, @@ -811,11 +828,10 @@ describe('Axis computational utils', () => { false, ); - left = 0 + 10 + 10; // no title + chart margin left + label width expect(axisTicksPosition.axisPositions.get(verticalAxisSpecWTitle.id)).toEqual({ top: 0, - left: 20, - width: 10, + left: 10, + width: 30, height: 100, }); }); @@ -842,8 +858,8 @@ describe('Axis computational utils', () => { const expectedLeftAxisPosition = { dimensions: { height: 100, - width: 10, - left: 40, + width: 40, + left: 20, top: 0, }, topIncrement: 0, @@ -878,7 +894,7 @@ describe('Axis computational utils', () => { const expectedRightAxisPosition = { dimensions: { height: 100, - width: 10, + width: 40, left: 110, top: 0, }, @@ -913,10 +929,11 @@ describe('Axis computational utils', () => { const expectedTopAxisPosition = { dimensions: { - height: 10, + height: + axis1Dims.maxLabelBboxHeight + axisTitleHeight + horizontalAxisSpec.tickSize + horizontalAxisSpec.tickPadding, width: 100, left: 0, - top: 30, + top: cumTopSum + LIGHT_THEME.chartMargins.top, }, topIncrement: 50, bottomIncrement: 0, @@ -949,7 +966,7 @@ describe('Axis computational utils', () => { const expectedBottomAxisPosition = { dimensions: { - height: 10, + height: 40, width: 100, left: 0, top: 110, @@ -975,7 +992,10 @@ describe('Axis computational utils', () => { axisDims.set(getAxisId('not_a_mapped_one'), axis1Dims); const axisTicksPosition = getAxisTicksPositions( - chartDim, + { + chartDimensions: chartDim, + leftMargin: 0, + }, LIGHT_THEME, chartRotation, showLegend, @@ -1006,7 +1026,10 @@ describe('Axis computational utils', () => { axisDims.set(verticalAxisSpec.id, axis1Dims); const axisTicksPosition = getAxisTicksPositions( - chartDim, + { + chartDimensions: chartDim, + leftMargin: 0, + }, LIGHT_THEME, chartRotation, showLegend, @@ -1036,7 +1059,10 @@ describe('Axis computational utils', () => { expect(axisTicksPosition.axisGridLinesPositions.get(verticalAxisSpec.id)).toEqual(expectedVerticalAxisGridLines); const axisTicksPositionWithTopLegend = getAxisTicksPositions( - chartDim, + { + chartDimensions: chartDim, + leftMargin: 0, + }, LIGHT_THEME, chartRotation, showLegend, @@ -1051,7 +1077,7 @@ describe('Axis computational utils', () => { const expectedPositionWithTopLegend = { height: 100, - width: 10, + width: 30, left: 100, top: 0, }; @@ -1063,7 +1089,10 @@ describe('Axis computational utils', () => { invalidSpecs.set(verticalAxisSpec.id, ungroupedAxisSpec); const computeScalelessSpec = () => { getAxisTicksPositions( - chartDim, + { + chartDimensions: chartDim, + leftMargin: 0, + }, LIGHT_THEME, chartRotation, showLegend, diff --git a/src/chart_types/xy_chart/utils/axis_utils.ts b/src/chart_types/xy_chart/utils/axis_utils.ts index 46d1f83218..f40705a0d4 100644 --- a/src/chart_types/xy_chart/utils/axis_utils.ts +++ b/src/chart_types/xy_chart/utils/axis_utils.ts @@ -253,7 +253,7 @@ export function centerRotationOrigin( * @param tickSize length of tick line * @param tickPadding amount of padding between label and tick line * @param tickPosition position of tick relative to axis line origin and other ticks along it - * @param axisPosition position of where the axis sits relative to the visualization + * @param position position of where the axis sits relative to the visualization * @param axisTicksDimensions computed axis dimensions and values (from computeTickDimensions) */ export function getTickLabelProps( @@ -261,31 +261,31 @@ export function getTickLabelProps( tickSize: number, tickPadding: number, tickPosition: number, - axisPosition: Position, + position: Position, + axisPosition: Dimensions, axisTicksDimensions: AxisTicksDimensions, ): TickLabelProps { const { maxLabelBboxWidth, maxLabelBboxHeight } = axisTicksDimensions; - const isVerticalAxis = isVertical(axisPosition); const isRotated = tickLabelRotation !== 0; let align = 'center'; let verticalAlign = 'middle'; - if (isVerticalAxis) { - const isAxisLeft = axisPosition === Position.Left; + if (isVertical(position)) { + const isLeftAxis = position === Position.Left; if (!isRotated) { - align = isAxisLeft ? 'right' : 'left'; + align = isLeftAxis ? 'right' : 'left'; } return { - x: isAxisLeft ? -maxLabelBboxWidth : tickSize + tickPadding, + x: isLeftAxis ? axisPosition.width - tickSize - tickPadding - maxLabelBboxWidth : tickSize + tickPadding, y: tickPosition - maxLabelBboxHeight / 2, align, verticalAlign, }; } - const isAxisTop = axisPosition === Position.Top; + const isAxisTop = position === Position.Top; if (!isRotated) { verticalAlign = isAxisTop ? 'bottom' : 'top'; @@ -293,7 +293,7 @@ export function getTickLabelProps( return { x: tickPosition - maxLabelBboxWidth / 2, - y: isAxisTop ? 0 : tickSize + tickPadding, + y: isAxisTop ? axisPosition.height - tickSize - tickPadding - maxLabelBboxHeight : tickSize + tickPadding, align, verticalAlign, }; @@ -301,29 +301,28 @@ export function getTickLabelProps( export function getVerticalAxisTickLineProps( position: Position, - tickPadding: number, + axisWidth: number, tickSize: number, tickPosition: number, ): AxisLinePosition { const isLeftAxis = position === Position.Left; const y = tickPosition; - const x1 = isLeftAxis ? tickPadding : 0; - const x2 = isLeftAxis ? tickSize + tickPadding : tickSize; + const x1 = isLeftAxis ? axisWidth : 0; + const x2 = isLeftAxis ? axisWidth - tickSize : tickSize; return [x1, y, x2, y]; } export function getHorizontalAxisTickLineProps( position: Position, - tickPadding: number, + axisHeight: number, tickSize: number, tickPosition: number, - labelHeight: number, ): AxisLinePosition { const isTopAxis = position === Position.Top; const x = tickPosition; - const y1 = isTopAxis ? labelHeight + tickPadding : 0; - const y2 = isTopAxis ? labelHeight + tickPadding + tickSize : tickSize; + const y1 = isTopAxis ? axisHeight - tickSize : 0; + const y2 = isTopAxis ? axisHeight : tickSize; return [x, y1, x, y2]; } @@ -505,30 +504,35 @@ export function getAxisPosition( let rightIncrement = 0; if (isVertical(position)) { + const dimWidth = maxLabelBboxWidth + tickSize + tickPadding + axisTitleHeight; if (position === Position.Left) { - leftIncrement = maxLabelBboxWidth + tickSize + tickPadding + chartMargins.left + axisTitleHeight; - dimensions.left = maxLabelBboxWidth + cumLeftSum + chartMargins.left + axisTitleHeight; + leftIncrement = dimWidth + chartMargins.left; + dimensions.left = cumLeftSum + chartMargins.left; } else { - rightIncrement = maxLabelBboxWidth + tickSize + tickPadding + chartMargins.right + axisTitleHeight; + rightIncrement = dimWidth + chartMargins.right; dimensions.left = left + width + cumRightSum; } - dimensions.width = maxLabelBboxWidth; + dimensions.width = dimWidth; } else { + const dimHeight = maxLabelBboxHeight + tickSize + tickPadding + axisTitleHeight; if (position === Position.Top) { - topIncrement = maxLabelBboxHeight + tickSize + tickPadding + chartMargins.top + axisTitleHeight; - dimensions.top = cumTopSum + chartMargins.top + axisTitleHeight; + topIncrement = dimHeight + chartMargins.top; + dimensions.top = cumTopSum + chartMargins.top; } else { - bottomIncrement = maxLabelBboxHeight + tickSize + tickPadding + chartMargins.bottom + axisTitleHeight; + bottomIncrement = dimHeight + chartMargins.bottom; dimensions.top = top + height + cumBottomSum; } - dimensions.height = maxLabelBboxHeight; + dimensions.height = dimHeight; } return { dimensions, topIncrement, bottomIncrement, leftIncrement, rightIncrement }; } export function getAxisTicksPositions( - chartDimensions: Dimensions, + computedChartDims: { + chartDimensions: Dimensions; + leftMargin: number; + }, chartTheme: Theme, chartRotation: Rotation, showLegend: boolean, @@ -547,32 +551,22 @@ export function getAxisTicksPositions( const axisVisibleTicks: Map = new Map(); const axisTicks: Map = new Map(); const axisGridLinesPositions: Map = new Map(); - + const { chartDimensions } = computedChartDims; let cumTopSum = 0; let cumBottomSum = chartPaddings.bottom; - let cumLeftSum = 0; + let cumLeftSum = computedChartDims.leftMargin; let cumRightSum = chartPaddings.right; if (showLegend) { switch (legendPosition) { case Position.Left: cumLeftSum += legendStyle.verticalWidth; break; - // case Position.Right: - // cumRightSum += legendStyle.verticalWidth; - // break; - // case Position.Bottom: - // cumBottomSum += legendStyle.horizontalHeight; - // break; case Position.Top: cumTopSum += legendStyle.horizontalHeight; break; } } - // console.log({cumRightSum}); - // let cumTopSum = showLegend ? legendStyle.horizontalHeight : 0; - // let cumBottomSum = chartConfig.paddings.bottom; - // let cumLeftSum = showLegend ? legendStyle.verticalWidth : 0; - // let cumRightSum = chartConfig.paddings.right; + axisDimensions.forEach((axisDim, id) => { const axisSpec = axisSpecs.get(id); @@ -615,7 +609,6 @@ export function getAxisTicksPositions( const { fontSize, padding } = chartTheme.axes.axisTitleStyle; const axisTitleHeight = axisSpec.title !== undefined ? fontSize + padding : 0; - const axisPosition = getAxisPosition( chartDimensions, chartMargins, diff --git a/src/chart_types/xy_chart/utils/dimensions.test.ts b/src/chart_types/xy_chart/utils/dimensions.test.ts index 809a680c6a..785c2c5f8c 100644 --- a/src/chart_types/xy_chart/utils/dimensions.test.ts +++ b/src/chart_types/xy_chart/utils/dimensions.test.ts @@ -67,7 +67,7 @@ describe('Computed chart dimensions', () => { test('should be equal to parent dimension with no axis minus margins', () => { const axisDims = new Map(); const axisSpecs = new Map(); - const chartDimensions = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); + const { chartDimensions } = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); expect(chartDimensions.left + chartDimensions.width).toBeLessThanOrEqual(parentDim.width); expect(chartDimensions.top + chartDimensions.height).toBeLessThanOrEqual(parentDim.height); expect(chartDimensions).toMatchSnapshot(); @@ -79,7 +79,7 @@ describe('Computed chart dimensions', () => { const axisSpecs = new Map(); axisDims.set(getAxisId('axis_1'), axis1Dims); axisSpecs.set(getAxisId('axis_1'), axisLeftSpec); - const chartDimensions = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); + const { chartDimensions } = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); expect(chartDimensions.left + chartDimensions.width).toBeLessThanOrEqual(parentDim.width); expect(chartDimensions.top + chartDimensions.height).toBeLessThanOrEqual(parentDim.height); expect(chartDimensions).toMatchSnapshot(); @@ -91,7 +91,7 @@ describe('Computed chart dimensions', () => { const axisSpecs = new Map(); axisDims.set(getAxisId('axis_1'), axis1Dims); axisSpecs.set(getAxisId('axis_1'), { ...axisLeftSpec, position: Position.Right }); - const chartDimensions = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); + const { chartDimensions } = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); expect(chartDimensions.left + chartDimensions.width).toBeLessThanOrEqual(parentDim.width); expect(chartDimensions.top + chartDimensions.height).toBeLessThanOrEqual(parentDim.height); expect(chartDimensions).toMatchSnapshot(); @@ -106,7 +106,7 @@ describe('Computed chart dimensions', () => { ...axisLeftSpec, position: Position.Top, }); - const chartDimensions = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); + const { chartDimensions } = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); expect(chartDimensions.left + chartDimensions.width).toBeLessThanOrEqual(parentDim.width); expect(chartDimensions.top + chartDimensions.height).toBeLessThanOrEqual(parentDim.height); expect(chartDimensions).toMatchSnapshot(); @@ -121,7 +121,7 @@ describe('Computed chart dimensions', () => { ...axisLeftSpec, position: Position.Bottom, }); - const chartDimensions = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); + const { chartDimensions } = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); expect(chartDimensions.left + chartDimensions.width).toBeLessThanOrEqual(parentDim.width); expect(chartDimensions.top + chartDimensions.height).toBeLessThanOrEqual(parentDim.height); expect(chartDimensions).toMatchSnapshot(); @@ -137,10 +137,13 @@ describe('Computed chart dimensions', () => { const chartDimensions = computeChartDimensions(parentDim, chartTheme, axisDims, axisSpecs, showLegend); const expectedDims = { - height: 60, - width: 60, - left: 20, - top: 20, + chartDimensions: { + height: 60, + width: 60, + left: 20, + top: 20, + }, + leftMargin: 10, }; expect(chartDimensions).toEqual(expectedDims); diff --git a/src/chart_types/xy_chart/utils/dimensions.ts b/src/chart_types/xy_chart/utils/dimensions.ts index 5ff1e0b82c..784782612e 100644 --- a/src/chart_types/xy_chart/utils/dimensions.ts +++ b/src/chart_types/xy_chart/utils/dimensions.ts @@ -21,7 +21,10 @@ export function computeChartDimensions( axisSpecs: Map, showLegend: boolean, legendPosition?: Position, -): Dimensions { +): { + chartDimensions: Dimensions; + leftMargin: number; +} { const { chartMargins, chartPaddings } = chartTheme; const legendStyle = chartTheme.legend; const { axisTitleStyle } = chartTheme.axes; @@ -32,7 +35,8 @@ export function computeChartDimensions( let vRightAxisSpecWidth = 0; let hTopAxisSpecHeight = 0; let hBottomAxisSpecHeight = 0; - + let horizontalEdgeLabelOverflow = 0; + let verticalEdgeLabelOverflow = 0; axisDimensions.forEach(({ maxLabelBboxWidth = 0, maxLabelBboxHeight = 0 }, id) => { const axisSpec = axisSpecs.get(id); if (!axisSpec || axisSpec.hide) { @@ -40,38 +44,41 @@ export function computeChartDimensions( } const { position, tickSize, tickPadding, title } = axisSpec; const titleHeight = title !== undefined ? axisTitleHeight : 0; + const maxAxisHeight = maxLabelBboxHeight + tickSize + tickPadding + titleHeight; + const maxAxisWidth = maxLabelBboxWidth + tickSize + tickPadding + titleHeight; switch (position) { case Position.Top: - hTopAxisSpecHeight += maxLabelBboxHeight + tickSize + tickPadding + chartMargins.top + titleHeight; + hTopAxisSpecHeight += maxAxisHeight + chartMargins.top; + // find the max half label size to accomodate the left/right labels + horizontalEdgeLabelOverflow = Math.max(horizontalEdgeLabelOverflow, maxLabelBboxWidth / 2); break; case Position.Bottom: - hBottomAxisSpecHeight += maxLabelBboxHeight + tickSize + tickPadding + chartMargins.bottom + titleHeight; + hBottomAxisSpecHeight += maxAxisHeight + chartMargins.bottom; + // find the max half label size to accomodate the left/right labels + horizontalEdgeLabelOverflow = Math.max(horizontalEdgeLabelOverflow, maxLabelBboxWidth / 2); break; case Position.Left: - vLeftAxisSpecWidth += maxLabelBboxWidth + tickSize + tickPadding + chartMargins.left + titleHeight; + vLeftAxisSpecWidth += maxAxisWidth + chartMargins.left; + verticalEdgeLabelOverflow = Math.max(verticalEdgeLabelOverflow, maxLabelBboxHeight / 2); break; case Position.Right: - vRightAxisSpecWidth += maxLabelBboxWidth + tickSize + tickPadding + chartMargins.right + titleHeight; + vRightAxisSpecWidth += maxAxisWidth + chartMargins.right; + verticalEdgeLabelOverflow = Math.max(verticalEdgeLabelOverflow, maxLabelBboxHeight / 2); break; } }); - // const hMargins = chartMargins.left + chartMargins.right; - const chartWidth = parentDimensions.width - vLeftAxisSpecWidth - vRightAxisSpecWidth; - const chartHeight = parentDimensions.height - hTopAxisSpecHeight - hBottomAxisSpecHeight; + const chartLeftAxisMaxWidth = Math.max(vLeftAxisSpecWidth, horizontalEdgeLabelOverflow + chartMargins.left); + const chartRightAxisMaxWidth = Math.max(vRightAxisSpecWidth, horizontalEdgeLabelOverflow + chartMargins.right); + const chartTopAxisMaxHeight = Math.max(hTopAxisSpecHeight, verticalEdgeLabelOverflow + chartMargins.top); + const chartBottomAxisMaxHeight = Math.max(hBottomAxisSpecHeight, verticalEdgeLabelOverflow + chartMargins.bottom); + + const chartWidth = parentDimensions.width - chartLeftAxisMaxWidth - chartRightAxisMaxWidth; + const chartHeight = parentDimensions.height - chartTopAxisMaxHeight - chartBottomAxisMaxHeight; + let vMargin = 0; - if (hTopAxisSpecHeight === 0) { - vMargin += chartMargins.top; - } - if (hBottomAxisSpecHeight === 0) { - vMargin += chartMargins.bottom; - } let hMargin = 0; - if (vLeftAxisSpecWidth === 0) { - hMargin += chartMargins.left; - } - if (vRightAxisSpecWidth === 0) { - hMargin += chartMargins.right; - } + + // add space for legend let legendTopMargin = 0; let legendLeftMargin = 0; if (showLegend) { @@ -92,22 +99,17 @@ export function computeChartDimensions( break; } } - let top = 0; - let left = 0; - if (hTopAxisSpecHeight === 0) { - top = chartMargins.top + chartPaddings.top + legendTopMargin; - } else { - top = hTopAxisSpecHeight + chartPaddings.top + legendTopMargin; - } - if (vLeftAxisSpecWidth === 0) { - left = chartMargins.left + chartPaddings.left + legendLeftMargin; - } else { - left = vLeftAxisSpecWidth + chartPaddings.left + legendLeftMargin; - } + + let top = chartTopAxisMaxHeight + chartPaddings.top + legendTopMargin; + let left = chartLeftAxisMaxWidth + chartPaddings.left + legendLeftMargin; + return { - top, - left, - width: chartWidth - hMargin - chartPaddings.left - chartPaddings.right, - height: chartHeight - vMargin - chartPaddings.top - chartPaddings.bottom, + leftMargin: chartLeftAxisMaxWidth - vLeftAxisSpecWidth, + chartDimensions: { + top, + left, + width: chartWidth - hMargin - chartPaddings.left - chartPaddings.right, + height: chartHeight - vMargin - chartPaddings.top - chartPaddings.bottom, + }, }; } diff --git a/src/components/react_canvas/axis.tsx b/src/components/react_canvas/axis.tsx index e447f368b9..7483ce766d 100644 --- a/src/components/react_canvas/axis.tsx +++ b/src/components/react_canvas/axis.tsx @@ -28,7 +28,68 @@ export class Axis extends React.PureComponent { render() { return this.renderAxis(); } - renderTickLabel = (tick: AxisTick, i: number) => { + + private renderAxis = () => { + const { ticks, axisPosition, debug } = this.props; + return ( + + {debug && ( + + )} + {this.renderAxisLine()} + {ticks.map(this.renderTickLine)} + {ticks.filter((tick) => tick.label !== null).map(this.renderTickLabel)} + {this.renderAxisTitle()} + + ); + }; + private renderAxisLine = () => { + const { + axisSpec: { position }, + axisPosition, + chartTheme: { + axes: { axisLineStyle }, + }, + } = this.props; + const lineProps: number[] = []; + if (isVertical(position)) { + lineProps[0] = position === Position.Left ? axisPosition.width : 0; + lineProps[2] = position === Position.Left ? axisPosition.width : 0; + lineProps[1] = 0; + lineProps[3] = axisPosition.height; + } else { + lineProps[0] = 0; + lineProps[2] = axisPosition.width; + lineProps[1] = position === Position.Top ? axisPosition.height : 0; + lineProps[3] = position === Position.Top ? axisPosition.height : 0; + } + return ; + }; + private renderTickLine = (tick: AxisTick, i: number) => { + const { + axisSpec: { tickSize, position }, + axisPosition, + chartTheme: { + axes: { tickLineStyle }, + }, + } = this.props; + + const lineProps = isVertical(position) + ? getVerticalAxisTickLineProps(position, axisPosition.width, tickSize, tick.position) + : getHorizontalAxisTickLineProps(position, axisPosition.height, tickSize, tick.position); + + return ; + }; + private renderTickLabel = (tick: AxisTick, i: number) => { /** * padding is already computed through width * and bbox_calculator using tickLabelPadding @@ -42,6 +103,7 @@ export class Axis extends React.PureComponent { const { axisSpec: { tickSize, tickPadding, position }, axisTicksDimensions, + axisPosition, debug, } = this.props; @@ -53,6 +115,7 @@ export class Axis extends React.PureComponent { tickPadding, tick.position, position, + axisPosition, axisTicksDimensions, ); @@ -73,62 +136,11 @@ export class Axis extends React.PureComponent { return ( - {debug && } + {debug && } ); }; - - private renderTickLine = (tick: AxisTick, i: number) => { - const { - axisSpec: { tickSize, tickPadding, position }, - axisTicksDimensions: { maxLabelBboxHeight }, - chartTheme: { - axes: { tickLineStyle }, - }, - } = this.props; - - const lineProps = isVertical(position) - ? getVerticalAxisTickLineProps(position, tickPadding, tickSize, tick.position) - : getHorizontalAxisTickLineProps(position, tickPadding, tickSize, tick.position, maxLabelBboxHeight); - - return ; - }; - private renderAxis = () => { - const { ticks, axisPosition, debug } = this.props; - return ( - - {debug && } - {this.renderAxisLine()} - {ticks.map(this.renderTickLine)} - {ticks.filter((tick) => tick.label !== null).map(this.renderTickLabel)} - {this.renderAxisTitle()} - - ); - }; - private renderAxisLine = () => { - const { - axisSpec: { tickSize, tickPadding, position }, - axisPosition, - axisTicksDimensions, - chartTheme: { - axes: { axisLineStyle }, - }, - } = this.props; - const lineProps: number[] = []; - if (isVertical(position)) { - lineProps[0] = position === Position.Left ? tickSize + tickPadding : 0; - lineProps[2] = position === Position.Left ? tickSize + tickPadding : 0; - lineProps[1] = 0; - lineProps[3] = axisPosition.height; - } else { - lineProps[0] = 0; - lineProps[2] = axisPosition.width; - lineProps[1] = position === Position.Top ? axisTicksDimensions.maxLabelBboxHeight + tickSize + tickPadding : 0; - lineProps[3] = position === Position.Top ? axisTicksDimensions.maxLabelBboxHeight + tickSize + tickPadding : 0; - } - return ; - }; private renderAxisTitle() { const { axisSpec: { title, position }, @@ -156,10 +168,7 @@ export class Axis extends React.PureComponent { } const { padding, ...titleStyle } = axisTitleStyle; const top = height; - const left = - position === Position.Left - ? -(maxLabelBboxWidth + titleStyle.fontSize + padding) - : tickSize + tickPadding + maxLabelBboxWidth + padding; + const left = position === Position.Left ? 0 : tickSize + tickPadding + maxLabelBboxWidth + padding; return ( @@ -172,6 +181,7 @@ export class Axis extends React.PureComponent { fill="violet" stroke="black" strokeWidth={1} + opacity={0.2} rotation={-90} /> )} @@ -196,8 +206,7 @@ export class Axis extends React.PureComponent { return; } - const top = - position === Position.Top ? -maxLabelBboxHeight - padding : maxLabelBboxHeight + tickPadding + tickSize + padding; + const top = position === Position.Top ? 0 : maxLabelBboxHeight + tickPadding + tickSize + padding; const left = 0; return ( diff --git a/stories/axis.tsx b/stories/axis.tsx index 0286749640..e3730461df 100644 --- a/stories/axis.tsx +++ b/stories/axis.tsx @@ -105,6 +105,7 @@ storiesOf('Axis', module) max: 90, step: 1, })} + hide={boolean('hide bottom axis', false)} style={customStyle} /> Number(d).toFixed(2)} style={customStyle} + hide={boolean('hide left axis', false)} /> Number(d).toFixed(2)} style={customStyle} + hide={boolean('hide top axis', false)} /> Number(d).toFixed(2)} style={customStyle} + hide={boolean('hide right axis', false)} />