diff --git a/src/chart/generateCategoricalChart.tsx b/src/chart/generateCategoricalChart.tsx index e380318ea2..62dff4c157 100644 --- a/src/chart/generateCategoricalChart.tsx +++ b/src/chart/generateCategoricalChart.tsx @@ -325,6 +325,7 @@ const getAxisMapByAxes = ( graphicalItems.filter((item: any) => item.props[axisIdKey] === axisId && !item.props.hide), dataKey, axisType, + layout, ); if (errorBarsDomain) { @@ -349,6 +350,7 @@ const getAxisMapByAxes = ( displayedData, graphicalItems.filter((item: any) => item.props[axisIdKey] === axisId && !item.props.hide), type, + layout, true, ); } @@ -439,6 +441,7 @@ const getAxisMapByItems = ( displayedData, graphicalItems.filter((item: any) => item.props[axisIdKey] === axisId && !item.props.hide), 'number', + layout, ), Axis.defaultProps.allowDataOverflow, ); diff --git a/src/util/ChartUtils.ts b/src/util/ChartUtils.ts index e377ea0bf5..459cae76c6 100644 --- a/src/util/ChartUtils.ts +++ b/src/util/ChartUtils.ts @@ -397,13 +397,39 @@ export const appendOffsetOfLegend = (offset: any, items: Array { - const { children } = item.props; - const errorBars = findAllByType(children, 'ErrorBar').filter((errorBarChild: any) => { - const { direction } = errorBarChild.props; +const isErrorBarRelevantForAxis = (layout?: LayoutType, axisType?: AxisType, direction?: 'x' | 'y') => { + if (_.isNil(axisType)) { + return true; + } - return _.isNil(direction) || _.isNil(axisType) ? true : axisType.indexOf(direction) >= 0; - }); + if (layout === 'horizontal') { + return axisType === 'yAxis'; + } + if (layout === 'vertical') { + return axisType === 'xAxis'; + } + + if (direction === 'x') { + return axisType === 'xAxis'; + } + if (direction === 'y') { + return axisType === 'yAxis'; + } + + return true; +}; + +export const getDomainOfErrorBars = ( + data: any[], + item: any, + dataKey: any, + layout?: LayoutType, + axisType?: AxisType, +) => { + const { children } = item.props; + const errorBars = findAllByType(children, 'ErrorBar').filter((errorBarChild: any) => + isErrorBarRelevantForAxis(layout, axisType, errorBarChild.props.direction), + ); if (errorBars && errorBars.length) { const keys = errorBars.map((errorBarChild: any) => errorBarChild.props.dataKey); @@ -431,9 +457,16 @@ export const getDomainOfErrorBars = (data: any[], item: any, dataKey: any, axisT return null; }; -export const parseErrorBarsOfAxis = (data: any[], items: any[], dataKey: any, axisType: AxisType) => { + +export const parseErrorBarsOfAxis = ( + data: any[], + items: any[], + dataKey: any, + axisType: AxisType, + layout?: LayoutType, +) => { const domains = items - .map(item => getDomainOfErrorBars(data, item, dataKey, axisType)) + .map(item => getDomainOfErrorBars(data, item, dataKey, layout, axisType)) .filter(entry => !_.isNil(entry)); if (domains && domains.length) { @@ -445,20 +478,28 @@ export const parseErrorBarsOfAxis = (data: any[], items: any[], dataKey: any, ax return null; }; + /** * Get domain of data by the configuration of item element * @param {Array} data The data displayed in the chart * @param {Array} items The instances of item * @param {String} type The type of axis, number - Number Axis, category - Category Axis + * @param {LayoutType} layout The type of layout * @param {Boolean} filterNil Whether or not filter nil values * @return {Array} Domain */ -export const getDomainOfItemsWithSameAxis = (data: any[], items: any[], type: string, filterNil?: boolean) => { +export const getDomainOfItemsWithSameAxis = ( + data: any[], + items: any[], + type: string, + layout?: LayoutType, + filterNil?: boolean, +) => { const domains = items.map(item => { const { dataKey } = item.props; if (type === 'number' && dataKey) { - return getDomainOfErrorBars(data, item, dataKey) || getDomainOfDataByKey(data, dataKey, type, filterNil); + return getDomainOfErrorBars(data, item, dataKey, layout) || getDomainOfDataByKey(data, dataKey, type, filterNil); } return getDomainOfDataByKey(data, dataKey, type, filterNil); }); diff --git a/test/specs/util/ChartUtilsSpec.js b/test/specs/util/ChartUtilsSpec.js index fa97c0327a..9f5e43b1f2 100644 --- a/test/specs/util/ChartUtilsSpec.js +++ b/test/specs/util/ChartUtilsSpec.js @@ -1,20 +1,23 @@ +import React from 'react'; import { expect } from 'chai'; import { scaleLinear, scaleBand } from 'd3-scale'; import { calculateActiveTickIndex, getDomainOfStackGroups, getDomainOfDataByKey, - appendOffsetOfLegend, getBandSizeOfAxis, calculateDomainOfTicks, parseSpecifiedDomain, parseScale, getTicksOfScale, getValueByDataKey, + getDomainOfErrorBars, offsetSign, MIN_VALUE_REG, MAX_VALUE_REG } from '../../../src/util/ChartUtils'; +import { Line, Bar, Scatter, Area, ErrorBar } from 'recharts'; +import { mount } from 'enzyme'; describe('getBandSizeOfAxis', () => { it('DataUtils.getBandSizeOfAxis() should return 0 ', () => { @@ -280,3 +283,127 @@ describe('getDomainOfDataByKey', () => { }); }); }); + +describe('getDomainOfErrorBars', () => { + const data = [ + { + x: 1, + y: 100, + error: 10, + error2: 15, + }, + { + x: 2, + y: 200, + error: 20, + error2: 15, + } + ]; + + describe('within Line component', () => { + const line = mount( + + + + ).instance(); + + describe('with horizontal layout', () => { + it('should not include error bars in xAxis domain', () => { + expect(getDomainOfErrorBars(data, line, 'x', 'horizontal', 'xAxis')).to.be.null; + }); + it('should include error bars in yAxis domain', () => { + expect(getDomainOfErrorBars(data, line, 'y', 'horizontal', 'yAxis')).to.deep.equal([90, 220]); + }); + }); + + describe('with vertical layout', () => { + it('should include error bars in xAxis domain', () => { + expect(getDomainOfErrorBars(data, line, 'x', 'vertical', 'xAxis')).to.deep.equal([-18, 22]); + }); + it('should not include error bars in yAxis domain', () => { + expect(getDomainOfErrorBars(data, line, 'y', 'vertical', 'yAxis')).to.be.null; + }); + }); + }); + + describe('within Bar component', () => { + const bar = mount( + + + + ).instance(); + + describe('with horizontal layout', () => { + it('should not include error bars in xAxis domain', () => { + expect(getDomainOfErrorBars(data, bar, 'x', 'horizontal', 'xAxis')).to.be.null; + }); + it('should include error bars in yAxis domain', () => { + expect(getDomainOfErrorBars(data, bar, 'y', 'horizontal', 'yAxis')).to.deep.equal([90, 220]); + }); + }); + + describe('with vertical layout', () => { + it('should include error bars in xAxis domain', () => { + expect(getDomainOfErrorBars(data, bar, 'x', 'vertical', 'xAxis')).to.deep.equal([-18, 22]); + }); + it('should not include error bars in yAxis domain', () => { + expect(getDomainOfErrorBars(data, bar, 'y', 'vertical', 'yAxis')).to.be.null; + }); + }); + }); + + describe('within Area component', () => { + const area = mount( + + + + ).instance(); + + describe('with horizontal layout', () => { + it('should not include error bars in xAxis domain', () => { + expect(getDomainOfErrorBars(data, area, 'x', 'horizontal', 'xAxis')).to.be.null; + }); + it('should include error bars in yAxis domain', () => { + expect(getDomainOfErrorBars(data, area, 'y', 'horizontal', 'yAxis')).to.deep.equal([90, 220]); + }); + }); + + describe('with vertical layout', () => { + it('should include error bars in xAxis domain', () => { + expect(getDomainOfErrorBars(data, area, 'x', 'vertical', 'xAxis')).to.deep.equal([-18, 22]); + }); + it('should not include error bars in yAxis domain', () => { + expect(getDomainOfErrorBars(data, area, 'y', 'vertical', 'yAxis')).to.be.null; + }); + }); + }); + + describe('within Scatter component', () => { + const scatter = mount( + + + + + ).instance(); + + it('should only include error bars with direction y in xAxis domain', () => { + expect(getDomainOfErrorBars(data, scatter, 'x', undefined, 'xAxis')).to.deep.equal([-14, 17]); + }); + it('should only include error bars with direction x in yAxis domain', () => { + expect(getDomainOfErrorBars(data, scatter, 'y', undefined, 'yAxis')).to.deep.equal([90, 220]); + }); + }); + + describe('with multiple ErrorBar children with same direction', () => { + const line = mount( + + + + + ).instance(); + + it('should return maximum domain of error bars', () => { + expect(getDomainOfErrorBars(data, line, 'y', 'horizontal', 'yAxis')).to.deep.equal([85, 220]); + }) + }); +});