From b603ee47daa6686d0e02ab2e875ea5af87972375 Mon Sep 17 00:00:00 2001 From: nickofthyme Date: Wed, 24 Jul 2019 14:12:34 -0400 Subject: [PATCH 1/4] feat(bar_chart): color overrides Allow user to override colors of given datum based on accessor function #216 --- src/lib/series/domains/y_domain.ts | 2 +- src/lib/series/rendering.ts | 17 +++++++++++------ src/lib/series/series.ts | 9 ++++----- src/lib/series/specs.ts | 6 ++++-- src/state/utils.ts | 1 + stories/bar_chart.tsx | 28 ++++++++++++++++++++++++++++ 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/lib/series/domains/y_domain.ts b/src/lib/series/domains/y_domain.ts index a0d87175af..b8928f7c99 100644 --- a/src/lib/series/domains/y_domain.ts +++ b/src/lib/series/domains/y_domain.ts @@ -16,7 +16,7 @@ export type YDomain = BaseDomain & { }; export type YBasicSeriesSpec = Pick< BasicSeriesSpec, - 'id' | 'seriesType' | 'yScaleType' | 'groupId' | 'stackAccessors' | 'yScaleToDataExtent' | 'colorAccessors' + 'id' | 'seriesType' | 'yScaleType' | 'groupId' | 'stackAccessors' | 'yScaleToDataExtent' | 'colorAccessor' > & { stackAsPercentage?: boolean }; export function mergeYDomain( diff --git a/src/lib/series/rendering.ts b/src/lib/series/rendering.ts index a9b29b77e3..517d760df3 100644 --- a/src/lib/series/rendering.ts +++ b/src/lib/series/rendering.ts @@ -16,7 +16,7 @@ import { CurveType, getCurveFactory } from './curves'; import { LegendItem } from './legend'; import { DataSeriesDatum } from './series'; import { belongsToDataSeries } from './series_utils'; -import { DisplayValueSpec } from './specs'; +import { DisplayValueSpec, ColorAccessor } from './specs'; export interface GeometryId { specId: SpecId; @@ -197,6 +197,7 @@ export function renderBars( seriesKey: any[], seriesStyle: BarSeriesStyle, displayValueSettings?: DisplayValueSpec, + colorAccessor?: ColorAccessor, ): { barGeometries: BarGeometry[]; indexedGeometries: Map; @@ -278,22 +279,26 @@ export function renderBars( } : undefined; + const geometryId = { + specId, + seriesKey, + }; + + const colorOverride = colorAccessor && colorAccessor(datum, geometryId); + const barGeometry: BarGeometry = { displayValue, x, y, // top most value width, height, - color, + color: colorOverride || color, value: { x: datum.x, y: initialY1, accessor: 'y1', }, - geometryId: { - specId, - seriesKey, - }, + geometryId, seriesStyle, }; mutableIndexedGeometryMapUpsert(indexedGeometries, datum.x, barGeometry); diff --git a/src/lib/series/series.ts b/src/lib/series/series.ts index dc6fff92fe..fd02da7525 100644 --- a/src/lib/series/series.ts +++ b/src/lib/series/series.ts @@ -91,7 +91,6 @@ export function splitSeries( xValues: Set; } { const { xAccessor, yAccessors, y0Accessors, splitSeriesAccessors = [] } = accessors; - const colorAccessors = accessors.colorAccessors ? accessors.colorAccessors : splitSeriesAccessors; const isMultipleY = yAccessors && yAccessors.length > 1; const series = new Map(); const colorsValues = new Map(); @@ -101,7 +100,7 @@ export function splitSeries( const seriesKey = getAccessorsValues(datum, splitSeriesAccessors); if (isMultipleY) { yAccessors.forEach((accessor, index) => { - const colorValues = getColorValues(datum, colorAccessors, accessor); + const colorValues = getColorValues(datum, splitSeriesAccessors, accessor); const colorValuesKey = getColorValuesAsString(colorValues, specId); colorsValues.set(colorValuesKey, colorValues); const cleanedDatum = cleanDatum(datum, xAccessor, accessor, y0Accessors && y0Accessors[index]); @@ -109,7 +108,7 @@ export function splitSeries( updateSeriesMap(series, [...seriesKey, accessor], cleanedDatum, specId, colorValuesKey); }, {}); } else { - const colorValues = getColorValues(datum, colorAccessors); + const colorValues = getColorValues(datum, splitSeriesAccessors); const colorValuesKey = getColorValuesAsString(colorValues, specId); colorsValues.set(colorValuesKey, colorValues); const cleanedDatum = cleanDatum(datum, xAccessor, yAccessors[0], y0Accessors && y0Accessors[0]); @@ -164,8 +163,8 @@ function getAccessorsValues(datum: Datum, accessors: Accessor[] = []): any[] { /** * Get the array of values that forms a series key */ -function getColorValues(datum: Datum, colorAccessors: Accessor[] = [], yAccessorValue?: any): any[] { - const colorValues = getAccessorsValues(datum, colorAccessors); +function getColorValues(datum: Datum, accessors: Accessor[] = [], yAccessorValue?: any): any[] { + const colorValues = getAccessorsValues(datum, accessors); if (yAccessorValue) { return [...colorValues, yAccessorValue]; } diff --git a/src/lib/series/specs.ts b/src/lib/series/specs.ts index 95599bc9a8..951785c676 100644 --- a/src/lib/series/specs.ts +++ b/src/lib/series/specs.ts @@ -11,11 +11,13 @@ import { Omit, RecursivePartial } from '../utils/commons'; import { AnnotationId, AxisId, GroupId, SpecId } from '../utils/ids'; import { ScaleContinuousType, ScaleType } from '../utils/scales/scales'; import { CurveType } from './curves'; -import { DataSeriesColorsValues } from './series'; +import { DataSeriesColorsValues, RawDataSeriesDatum } from './series'; +import { GeometryId } from './rendering'; export type Datum = any; export type Rotation = 0 | 90 | -90 | 180; export type Rendering = 'canvas' | 'svg'; +export type ColorAccessor = (datum: RawDataSeriesDatum, geometryId: GeometryId) => string | null; interface DomainMinInterval { /** Custom minInterval for the domain which will affect data bucket size. @@ -97,7 +99,7 @@ export interface SeriesAccessors { /** An array of fields thats indicates the stack membership */ stackAccessors?: Accessor[]; /** An optional array of field name thats indicates the stack membership */ - colorAccessors?: Accessor[]; + colorAccessor?: ColorAccessor; } export interface SeriesScales { diff --git a/src/state/utils.ts b/src/state/utils.ts index 758e06d948..4993b2f1a2 100644 --- a/src/state/utils.ts +++ b/src/state/utils.ts @@ -449,6 +449,7 @@ export function renderGeometries( ds.key, barSeriesStyle, displayValueSettings, + spec.colorAccessor, ); barGeometriesIndex = mergeGeometriesIndexes(barGeometriesIndex, renderedBars.indexedGeometries); bars.push(...renderedBars.barGeometries); diff --git a/stories/bar_chart.tsx b/stories/bar_chart.tsx index 743ac4ffbb..cc734b4f4b 100644 --- a/stories/bar_chart.tsx +++ b/stories/bar_chart.tsx @@ -33,6 +33,7 @@ import * as TestDatasets from '../src/lib/series/utils/test_dataset'; import { KIBANA_METRICS } from '../src/lib/series/utils/test_dataset_kibana'; import { TEST_DATASET_DISCOVER } from '../src/lib/series/utils/test_dataset_discover_per_30s'; +import { ColorAccessor } from '../src/lib/series/specs'; const dateFormatter = timeFormatter('HH:mm:ss'); @@ -1787,4 +1788,31 @@ storiesOf('Bar Chart', module) /> ); + }) + .add('colorAccessor overrides', () => { + const hasThreshold = boolean('threshold', true); + const threshold = number('min threshold', 4); + const colorAccessor: ColorAccessor = (d, g) => (g.specId === getSpecId('bars') && d.y1! > threshold ? 'red' : null); + + return ( + + + Number(d).toFixed(2)} + /> + + + + ); }); From b258767c42a0771a22aa822bfe8cefa4b5e48c4f Mon Sep 17 00:00:00 2001 From: nickofthyme Date: Tue, 30 Jul 2019 11:36:44 -0500 Subject: [PATCH 2/4] refactor: allow color and style overrides --- src/lib/series/domains/y_domain.ts | 2 +- src/lib/series/rendering.ts | 42 +++++++++++++++++++++++++----- src/lib/series/specs.ts | 6 +++-- src/state/utils.ts | 2 +- stories/bar_chart.tsx | 16 +++++++++--- wiki/overview.md | 19 +++++++------- 6 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/lib/series/domains/y_domain.ts b/src/lib/series/domains/y_domain.ts index b8928f7c99..730a7cb7f1 100644 --- a/src/lib/series/domains/y_domain.ts +++ b/src/lib/series/domains/y_domain.ts @@ -16,7 +16,7 @@ export type YDomain = BaseDomain & { }; export type YBasicSeriesSpec = Pick< BasicSeriesSpec, - 'id' | 'seriesType' | 'yScaleType' | 'groupId' | 'stackAccessors' | 'yScaleToDataExtent' | 'colorAccessor' + 'id' | 'seriesType' | 'yScaleType' | 'groupId' | 'stackAccessors' | 'yScaleToDataExtent' | 'styleAccessor' > & { stackAsPercentage?: boolean }; export function mergeYDomain( diff --git a/src/lib/series/rendering.ts b/src/lib/series/rendering.ts index 517d760df3..d49f2e6717 100644 --- a/src/lib/series/rendering.ts +++ b/src/lib/series/rendering.ts @@ -16,7 +16,8 @@ import { CurveType, getCurveFactory } from './curves'; import { LegendItem } from './legend'; import { DataSeriesDatum } from './series'; import { belongsToDataSeries } from './series_utils'; -import { DisplayValueSpec, ColorAccessor } from './specs'; +import { DisplayValueSpec, StyleAccessor } from './specs'; +import { mergePartial } from '../utils/commons'; export interface GeometryId { specId: SpecId; @@ -113,6 +114,33 @@ export function mutableIndexedGeometryMapUpsert( } } +function getStyleOverrides( + styleAccessor: StyleAccessor | null = null, + datum: DataSeriesDatum, + geometryId: GeometryId, + seriesStyle: BarSeriesStyle, +): BarSeriesStyle { + const styleOverride = styleAccessor && styleAccessor(datum, geometryId); + + if (styleOverride) { + if (typeof styleOverride === 'string') { + return { + ...seriesStyle, + rect: { + ...seriesStyle.rect, + fill: styleOverride, + }, + }; + } else { + seriesStyle = mergePartial(seriesStyle, styleOverride, { + mergeOptionalPartialValues: true, + }); + } + } + + return seriesStyle; +} + export function renderPoints( shift: number, dataset: DataSeriesDatum[], @@ -195,9 +223,9 @@ export function renderBars( color: string, specId: SpecId, seriesKey: any[], - seriesStyle: BarSeriesStyle, + sharedSeriesStyle: BarSeriesStyle, displayValueSettings?: DisplayValueSpec, - colorAccessor?: ColorAccessor, + styleAccessor?: StyleAccessor, ): { barGeometries: BarGeometry[]; indexedGeometries: Map; @@ -211,8 +239,8 @@ export function renderBars( // default padding to 1 for now const padding = 1; - const fontSize = seriesStyle.displayValue.fontSize; - const fontFamily = seriesStyle.displayValue.fontFamily; + const fontSize = sharedSeriesStyle.displayValue.fontSize; + const fontFamily = sharedSeriesStyle.displayValue.fontFamily; dataset.forEach((datum) => { const { y0, y1, initialY1 } = datum; @@ -284,7 +312,7 @@ export function renderBars( seriesKey, }; - const colorOverride = colorAccessor && colorAccessor(datum, geometryId); + const seriesStyle = getStyleOverrides(styleAccessor, datum, geometryId, sharedSeriesStyle); const barGeometry: BarGeometry = { displayValue, @@ -292,7 +320,7 @@ export function renderBars( y, // top most value width, height, - color: colorOverride || color, + color, value: { x: datum.x, y: initialY1, diff --git a/src/lib/series/specs.ts b/src/lib/series/specs.ts index 951785c676..9a4c96ccc0 100644 --- a/src/lib/series/specs.ts +++ b/src/lib/series/specs.ts @@ -17,7 +17,9 @@ import { GeometryId } from './rendering'; export type Datum = any; export type Rotation = 0 | 90 | -90 | 180; export type Rendering = 'canvas' | 'svg'; -export type ColorAccessor = (datum: RawDataSeriesDatum, geometryId: GeometryId) => string | null; +export type Color = string; +export type StyleOverride = RecursivePartial | Color | null; +export type StyleAccessor = (datum: RawDataSeriesDatum, geometryId: GeometryId) => StyleOverride; interface DomainMinInterval { /** Custom minInterval for the domain which will affect data bucket size. @@ -99,7 +101,7 @@ export interface SeriesAccessors { /** An array of fields thats indicates the stack membership */ stackAccessors?: Accessor[]; /** An optional array of field name thats indicates the stack membership */ - colorAccessor?: ColorAccessor; + styleAccessor?: StyleAccessor; } export interface SeriesScales { diff --git a/src/state/utils.ts b/src/state/utils.ts index 4993b2f1a2..6cbe3362bf 100644 --- a/src/state/utils.ts +++ b/src/state/utils.ts @@ -449,7 +449,7 @@ export function renderGeometries( ds.key, barSeriesStyle, displayValueSettings, - spec.colorAccessor, + spec.styleAccessor, ); barGeometriesIndex = mergeGeometriesIndexes(barGeometriesIndex, renderedBars.indexedGeometries); bars.push(...renderedBars.barGeometries); diff --git a/stories/bar_chart.tsx b/stories/bar_chart.tsx index cc734b4f4b..8cf53acf6e 100644 --- a/stories/bar_chart.tsx +++ b/stories/bar_chart.tsx @@ -27,13 +27,15 @@ import { Settings, timeFormatter, TooltipType, + BarSeriesStyle, + RecursivePartial, } from '../src/'; import * as TestDatasets from '../src/lib/series/utils/test_dataset'; import { KIBANA_METRICS } from '../src/lib/series/utils/test_dataset_kibana'; import { TEST_DATASET_DISCOVER } from '../src/lib/series/utils/test_dataset_discover_per_30s'; -import { ColorAccessor } from '../src/lib/series/specs'; +import { StyleAccessor } from '../src/lib/series/specs'; const dateFormatter = timeFormatter('HH:mm:ss'); @@ -1789,10 +1791,16 @@ storiesOf('Bar Chart', module) ); }) - .add('colorAccessor overrides', () => { + .add('styleAccessor overrides', () => { const hasThreshold = boolean('threshold', true); const threshold = number('min threshold', 4); - const colorAccessor: ColorAccessor = (d, g) => (g.specId === getSpecId('bars') && d.y1! > threshold ? 'red' : null); + const style: RecursivePartial = { + rect: { + opacity: 0.5, + fill: 'red', + }, + }; + const styleAccessor: StyleAccessor = (d, g) => (g.specId === getSpecId('bars') && d.y1! > threshold ? style : null); return ( @@ -1810,7 +1818,7 @@ storiesOf('Bar Chart', module) yScaleType={ScaleType.Linear} xAccessor="x" yAccessors={['y']} - colorAccessor={hasThreshold ? colorAccessor : undefined} + styleAccessor={hasThreshold ? styleAccessor : undefined} data={[{ x: 0, y: 2 }, { x: 1, y: 7 }, { x: 2, y: 3 }, { x: 3, y: 6 }]} /> diff --git a/wiki/overview.md b/wiki/overview.md index 1a0fe3106c..e8fe1d8882 100644 --- a/wiki/overview.md +++ b/wiki/overview.md @@ -214,14 +214,15 @@ Small multiples are created using the `` component, that takes m In the case of small multiples, each `SplittedSeries` computes its own x and y domains. Then the x domains are merged and expanded. The same happens with the main Y domains; they are merged together. -### Colors +### Color/Style overrides -Each data series can have its own color. -The color is assigned through the `colorAccessors` prop that specifies which data attributes are used to define the color, -for example: +Each `datum` of a **Bar Chart** data series can be assigned a custom color or style with the `styleAccessor` prop. -- a dataset without any split accessor or fields that can be used to identify a color will have a single color. -- a dataset that has 1 variable to split the dataset into 3 different series, will have 3 different colors if that variable is specified through the `colorAccessors`. -- a dataset with 2 split variables, each with 3 different values (a dataset with 3 \* 2 series) will have 6 different colors if the two variables are defined in `colorAccessors` -- a dataset with 2 split variables, each with 3 different values, but only one specified in the `colorAccessors` will have only 3 colors. -- if no `colorAccessors` is specified, `splitAccessors` will be used to identify how to coloring the series +The `styleAccessor` prop expects a callback function which will be called on _every_ `datum` in _every_ bar series with the signiture below. This callback should return a color `string` or a partial `BarSeriesStyle`, which will override any series-level styles for the respective `datum`. You are passed `geometryId` to identify the series the `datum` belongs to and the raw `datum` to derive conditions against. + +```ts +type StyleAccessor = ( + datum: RawDataSeriesDatum, + geometryId: GeometryId, +) => RecursivePartial | Color | null; +``` From 8dc3b5dbcbe8103c78c3396958e83586c478c78d Mon Sep 17 00:00:00 2001 From: nickofthyme Date: Tue, 30 Jul 2019 20:52:07 -0500 Subject: [PATCH 3/4] test(rendering): getStyleOverrides method --- .../xy_chart/rendering/rendering.test.ts | 118 +++++++++++++++++- .../xy_chart/rendering/rendering.ts | 2 +- 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/chart_types/xy_chart/rendering/rendering.test.ts b/src/chart_types/xy_chart/rendering/rendering.test.ts index 72f6180134..a67042e9c1 100644 --- a/src/chart_types/xy_chart/rendering/rendering.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.test.ts @@ -1,6 +1,16 @@ import { DEFAULT_GEOMETRY_STYLES } from '../../../utils/themes/theme_commons'; import { getSpecId } from '../../../utils/ids'; -import { BarGeometry, getGeometryStyle, isPointOnGeometry, PointGeometry } from './rendering'; +import { + BarGeometry, + getGeometryStyle, + isPointOnGeometry, + PointGeometry, + getStyleOverrides, + GeometryId, +} from './rendering'; +import { BarSeriesStyle } from '../../../utils/themes/theme'; +import { DataSeriesDatum } from '../utils/series'; +import { RecursivePartial, mergePartial } from '../../../utils/commons'; describe('Rendering utils', () => { test('check if point is in geometry', () => { @@ -168,4 +178,110 @@ describe('Rendering utils', () => { expect(noHover).toEqual({ opacity: 1 }); }); + + describe('getStyleOverrides', () => { + let mockAccessor: jest.Mock; + + const sampleSeriesStyle: BarSeriesStyle = { + rect: { + opacity: 1, + }, + rectBorder: { + visible: true, + strokeWidth: 1, + }, + displayValue: { + fontSize: 10, + fontFamily: 'helvetica', + fill: 'blue', + padding: 1, + offsetX: 1, + offsetY: 1, + }, + }; + const datum: DataSeriesDatum = { + x: 1, + y1: 2, + y0: 3, + initialY1: 4, + initialY0: 5, + }; + const geometryId: GeometryId = { + specId: getSpecId('test'), + seriesKey: ['test'], + }; + + beforeEach(() => { + mockAccessor = jest.fn(); + }); + + it('should return input seriesStyle if styleAccessor is null', () => { + const styleOverrides = getStyleOverrides(null, datum, geometryId, sampleSeriesStyle); + + expect(styleOverrides).toBe(sampleSeriesStyle); + }); + + it('should return input seriesStyle if styleAccessor returns null', () => { + mockAccessor.mockReturnValue(null); + const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + + expect(styleOverrides).toBe(sampleSeriesStyle); + }); + + it('should call styleAccessor with datum and geometryId', () => { + getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + + expect(mockAccessor).toBeCalledWith(datum, geometryId); + }); + + it('should return seriesStyle with updated fill color', () => { + const color = 'blue'; + mockAccessor.mockReturnValue(color); + const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const expectedStyles: BarSeriesStyle = { + ...sampleSeriesStyle, + rect: { + ...sampleSeriesStyle.rect, + fill: color, + }, + }; + expect(styleOverrides).toEqual(expectedStyles); + }); + + it('should return a new seriesStyle object with color', () => { + mockAccessor.mockReturnValue('blue'); + const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + + expect(styleOverrides).not.toBe(sampleSeriesStyle); + }); + + it('should return seriesStyle with updated partial style', () => { + const partialStyle: RecursivePartial = { + rect: { + fill: 'blue', + }, + rectBorder: { + strokeWidth: 10, + }, + }; + mockAccessor.mockReturnValue(partialStyle); + const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const expectedStyles = mergePartial(sampleSeriesStyle, partialStyle, { + mergeOptionalPartialValues: true, + }); + + expect(styleOverrides).toEqual(expectedStyles); + }); + + it('should return a new seriesStyle object with partial styles', () => { + mockAccessor.mockReturnValue({ + rect: { + fill: 'blue', + }, + }); + const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + + expect(styleOverrides).not.toBe(sampleSeriesStyle); + }); + }); }); diff --git a/src/chart_types/xy_chart/rendering/rendering.ts b/src/chart_types/xy_chart/rendering/rendering.ts index 5bc1d787c6..c1fa51c331 100644 --- a/src/chart_types/xy_chart/rendering/rendering.ts +++ b/src/chart_types/xy_chart/rendering/rendering.ts @@ -115,7 +115,7 @@ export function mutableIndexedGeometryMapUpsert( } } -function getStyleOverrides( +export function getStyleOverrides( styleAccessor: StyleAccessor | null = null, datum: DataSeriesDatum, geometryId: GeometryId, From d5ae861e40f945186799c2626a12273509e90bb4 Mon Sep 17 00:00:00 2001 From: nickofthyme Date: Thu, 1 Aug 2019 09:32:41 -0500 Subject: [PATCH 4/4] refactor: optional style, docs, break early --- .../xy_chart/rendering/rendering.test.ts | 16 ++++----- .../xy_chart/rendering/rendering.ts | 34 +++++++++---------- src/chart_types/xy_chart/utils/specs.ts | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/chart_types/xy_chart/rendering/rendering.test.ts b/src/chart_types/xy_chart/rendering/rendering.test.ts index a67042e9c1..0d1352809d 100644 --- a/src/chart_types/xy_chart/rendering/rendering.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.test.ts @@ -215,21 +215,21 @@ describe('Rendering utils', () => { mockAccessor = jest.fn(); }); - it('should return input seriesStyle if styleAccessor is null', () => { - const styleOverrides = getStyleOverrides(null, datum, geometryId, sampleSeriesStyle); + it('should return input seriesStyle if no styleAccessor is passed', () => { + const styleOverrides = getStyleOverrides(datum, geometryId, sampleSeriesStyle); expect(styleOverrides).toBe(sampleSeriesStyle); }); it('should return input seriesStyle if styleAccessor returns null', () => { mockAccessor.mockReturnValue(null); - const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const styleOverrides = getStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); expect(styleOverrides).toBe(sampleSeriesStyle); }); it('should call styleAccessor with datum and geometryId', () => { - getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + getStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); expect(mockAccessor).toBeCalledWith(datum, geometryId); }); @@ -237,7 +237,7 @@ describe('Rendering utils', () => { it('should return seriesStyle with updated fill color', () => { const color = 'blue'; mockAccessor.mockReturnValue(color); - const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const styleOverrides = getStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); const expectedStyles: BarSeriesStyle = { ...sampleSeriesStyle, rect: { @@ -250,7 +250,7 @@ describe('Rendering utils', () => { it('should return a new seriesStyle object with color', () => { mockAccessor.mockReturnValue('blue'); - const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const styleOverrides = getStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); expect(styleOverrides).not.toBe(sampleSeriesStyle); }); @@ -265,7 +265,7 @@ describe('Rendering utils', () => { }, }; mockAccessor.mockReturnValue(partialStyle); - const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const styleOverrides = getStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); const expectedStyles = mergePartial(sampleSeriesStyle, partialStyle, { mergeOptionalPartialValues: true, }); @@ -279,7 +279,7 @@ describe('Rendering utils', () => { fill: 'blue', }, }); - const styleOverrides = getStyleOverrides(mockAccessor, datum, geometryId, sampleSeriesStyle); + const styleOverrides = getStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); expect(styleOverrides).not.toBe(sampleSeriesStyle); }); diff --git a/src/chart_types/xy_chart/rendering/rendering.ts b/src/chart_types/xy_chart/rendering/rendering.ts index c1fa51c331..8c1282f025 100644 --- a/src/chart_types/xy_chart/rendering/rendering.ts +++ b/src/chart_types/xy_chart/rendering/rendering.ts @@ -116,30 +116,30 @@ export function mutableIndexedGeometryMapUpsert( } export function getStyleOverrides( - styleAccessor: StyleAccessor | null = null, datum: DataSeriesDatum, geometryId: GeometryId, seriesStyle: BarSeriesStyle, + styleAccessor?: StyleAccessor, ): BarSeriesStyle { const styleOverride = styleAccessor && styleAccessor(datum, geometryId); - if (styleOverride) { - if (typeof styleOverride === 'string') { - return { - ...seriesStyle, - rect: { - ...seriesStyle.rect, - fill: styleOverride, - }, - }; - } else { - seriesStyle = mergePartial(seriesStyle, styleOverride, { - mergeOptionalPartialValues: true, - }); - } + if (!styleOverride) { + return seriesStyle; } - return seriesStyle; + if (typeof styleOverride === 'string') { + return { + ...seriesStyle, + rect: { + ...seriesStyle.rect, + fill: styleOverride, + }, + }; + } + + return mergePartial(seriesStyle, styleOverride, { + mergeOptionalPartialValues: true, + }); } export function renderPoints( @@ -313,7 +313,7 @@ export function renderBars( seriesKey, }; - const seriesStyle = getStyleOverrides(styleAccessor, datum, geometryId, sharedSeriesStyle); + const seriesStyle = getStyleOverrides(datum, geometryId, sharedSeriesStyle, styleAccessor); const barGeometry: BarGeometry = { displayValue, diff --git a/src/chart_types/xy_chart/utils/specs.ts b/src/chart_types/xy_chart/utils/specs.ts index 073589a307..90f9abd0e1 100644 --- a/src/chart_types/xy_chart/utils/specs.ts +++ b/src/chart_types/xy_chart/utils/specs.ts @@ -100,7 +100,7 @@ export interface SeriesAccessors { splitSeriesAccessors?: Accessor[]; /** An array of fields thats indicates the stack membership */ stackAccessors?: Accessor[]; - /** An optional array of field name thats indicates the stack membership */ + /** An optional functional accessor to return custom datum color or style */ styleAccessor?: StyleAccessor; }