diff --git a/.storybook/utils.ts b/.storybook/utils.ts deleted file mode 100644 index b40f42cbd6..0000000000 --- a/.storybook/utils.ts +++ /dev/null @@ -1,11 +0,0 @@ -import seedrandom from 'seedrandom'; - -import { DataGenerator } from '../src'; - -export const getRandomNumber = seedrandom(process.env.RNG_SEED || undefined); - -export class SeededDataGenerator extends DataGenerator { - constructor(frequency = 500) { - super(frequency, getRandomNumber); - } -} diff --git a/integration/helpers.ts b/integration/helpers.ts index 128326a1f4..f2d21b2f8c 100644 --- a/integration/helpers.ts +++ b/integration/helpers.ts @@ -23,6 +23,8 @@ function requireAllStories() { function encodeString(string: string) { return string + .replace(/-/g, ' ') + .replace(/\w-\w/g, ' ') .replace(/\//gi, ' ') .replace(/-/g, ' ') .replace(/[^a-z|A-Z|0-9|\s|\/]+/gi, '') diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-bar-chart-tooltip-series-visibility-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-bar-chart-tooltip-series-visibility-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000..04ad1adad4 Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-bar-chart-tooltip-series-visibility-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-full-and-sub-series-label-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-full-and-sub-series-label-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000..aa0cd39a38 Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-full-and-sub-series-label-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-sub-series-label-formatting-time-date-and-percent-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-sub-series-label-formatting-time-date-and-percent-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000..e779acf24f Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-add-custom-sub-series-label-formatting-time-date-and-percent-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-colors-via-accessor-function-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-colors-via-accessor-function-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000..f9881e56fa Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-colors-via-accessor-function-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-colors-via-colors-array-visually-looks-correct-1-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-colors-via-colors-array-visually-looks-correct-1-snap.png new file mode 100644 index 0000000000..6c79957c57 Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-custom-series-colors-via-colors-array-visually-looks-correct-1-snap.png differ diff --git a/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-tick-label-padding-both-prop-and-theme-visually-looks-correct-2-snap.png b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-tick-label-padding-both-prop-and-theme-visually-looks-correct-2-snap.png new file mode 100644 index 0000000000..8e19b41b86 Binary files /dev/null and b/integration/tests/__image_snapshots__/all-test-ts-baseline-visual-tests-for-all-stories-stylings-tick-label-padding-both-prop-and-theme-visually-looks-correct-2-snap.png differ diff --git a/package.json b/package.json index a95a323511..73b7cb9b94 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "ts-loader": "^6.1.2", "ts-prune": "^0.3.0", "typescript": "^3.6.3", - "utility-types": "^3.8.0", + "utility-types": "^3.9.0", "webpack": "^4.29.5", "webpack-cli": "^3.3.1", "webpack-dev-server": "^3.3.1" diff --git a/src/chart_types/xy_chart/domains/y_domain.test.ts b/src/chart_types/xy_chart/domains/y_domain.test.ts index 8e2a3d3303..2ae041c187 100644 --- a/src/chart_types/xy_chart/domains/y_domain.test.ts +++ b/src/chart_types/xy_chart/domains/y_domain.test.ts @@ -17,14 +17,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -58,22 +62,28 @@ describe('Y Domain', () => { const dataSeries1: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; const dataSeries2: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 10 }, { x: 2, y1: 10 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, ]; @@ -123,22 +133,28 @@ describe('Y Domain', () => { const dataSeries1: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; const dataSeries2: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 10 }, { x: 2, y1: 10 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, ]; @@ -181,22 +197,28 @@ describe('Y Domain', () => { const dataSeries1: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; const dataSeries2: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 10 }, { x: 2, y1: 10 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, ]; @@ -239,22 +261,28 @@ describe('Y Domain', () => { const dataSeries1: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: new Array(maxValues).fill(0).map((d, i) => ({ x: i, y1: i })), }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: new Array(maxValues).fill(0).map((d, i) => ({ x: i, y1: i })), }, ]; const dataSeries2: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: new Array(maxValues).fill(0).map((d, i) => ({ x: i, y1: i })), }, ]; @@ -460,14 +488,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -494,14 +526,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -540,14 +576,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -586,14 +626,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -628,14 +672,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -674,14 +722,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; @@ -714,22 +766,28 @@ describe('Y Domain', () => { const dataSeries1: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; const dataSeries2: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 10 }, { x: 2, y1: 10 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, ]; @@ -774,14 +832,18 @@ describe('Y Domain', () => { const dataSeries: RawDataSeries[] = [ { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 2, y1: 2 }, { x: 3, y1: 2 }, { x: 4, y1: 5 }], }, { specId: 'a', - key: [''], - seriesColorKey: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [''], + key: '', data: [{ x: 1, y1: 2 }, { x: 4, y1: 7 }], }, ]; diff --git a/src/chart_types/xy_chart/legend/legend.test.ts b/src/chart_types/xy_chart/legend/legend.test.ts index e931947703..835be8245e 100644 --- a/src/chart_types/xy_chart/legend/legend.test.ts +++ b/src/chart_types/xy_chart/legend/legend.test.ts @@ -1,7 +1,7 @@ import { getAxisId, getGroupId, getSpecId } from '../../../utils/ids'; import { ScaleType } from '../../../utils/scales/scales'; -import { computeLegend, getSeriesColorLabel } from './legend'; -import { DataSeriesColorsValues } from '../utils/series'; +import { computeLegend } from './legend'; +import { SeriesCollectionValue, getSeriesLabel } from '../utils/series'; import { AxisSpec, BasicSeriesSpec, Position, SpecTypes, SeriesTypes } from '../utils/specs'; import { ChartTypes } from '../..'; @@ -15,21 +15,41 @@ const nullDisplayValue = { y1: null, }, }; -const colorValues1a = { - specId: getSpecId('spec1'), - colorValues: [], +const seriesCollectionValue1a = { + seriesIdentifier: { + specId: getSpecId('spec1'), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['y1'], + key: 'seriesCollectionValue1a', + }, }; -const colorValues1b = { - specId: getSpecId('spec1'), - colorValues: ['a', 'b'], +const seriesCollectionValue1b = { + seriesIdentifier: { + specId: getSpecId('spec1'), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'y1'], + key: 'seriesCollectionValue1b', + }, }; -const colorValues2a = { - specId: getSpecId('spec2'), - colorValues: [], +const seriesCollectionValue2a = { + seriesIdentifier: { + specId: getSpecId('spec2'), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['y1'], + key: 'seriesCollectionValue2a', + }, }; -const colorValues2b = { - specId: getSpecId('spec3'), - colorValues: ['c', 'd'], +const seriesCollectionValue2b = { + seriesIdentifier: { + specId: getSpecId('spec3'), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['c', 'd', 'y1'], + key: 'seriesCollectionValue2b', + }, }; const spec1: BasicSeriesSpec = { chartType: ChartTypes.XYAxis, @@ -80,214 +100,267 @@ const axisSpec: AxisSpec = { axesSpecs.push(axisSpec); describe('Legends', () => { - const seriesColor = new Map(); - const seriesColorMap = new Map(); + const seriesCollection = new Map(); + const seriesCollectionMap = new Map(); const specs = [spec1, spec2]; - seriesColorMap.set('colorSeries1a', 'red'); - seriesColorMap.set('colorSeries1b', 'blue'); - seriesColorMap.set('colorSeries2a', 'green'); - seriesColorMap.set('colorSeries2b', 'white'); + seriesCollectionMap.set('seriesCollectionValue1a', 'red'); + seriesCollectionMap.set('seriesCollectionValue1b', 'blue'); + seriesCollectionMap.set('seriesCollectionValue2a', 'green'); + seriesCollectionMap.set('seriesCollectionValue2b', 'white'); beforeEach(() => { - seriesColor.clear(); + seriesCollection.clear(); }); it('compute legend for a single series', () => { - seriesColor.set('colorSeries1a', colorValues1a); - const legend = computeLegend(seriesColor, seriesColorMap, specs, 'violet', axesSpecs); + seriesCollection.set('seriesCollectionValue1a', seriesCollectionValue1a); + const legend = computeLegend(seriesCollection, seriesCollectionMap, specs, 'violet', axesSpecs); const expected = [ { color: 'red', label: 'Spec 1 title', - value: { colorValues: [], specId: 'spec1' }, + seriesIdentifier: { + seriesKeys: ['y1'], + specId: 'spec1', + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'seriesCollectionValue1a', + }, isSeriesVisible: true, isLegendItemVisible: true, - key: 'colorSeries1a', + key: 'seriesCollectionValue1a', displayValue: nullDisplayValue, + banded: undefined, }, ]; expect(Array.from(legend.values())).toEqual(expected); }); it('compute legend for a single spec but with multiple series', () => { - seriesColor.set('colorSeries1a', colorValues1a); - seriesColor.set('colorSeries1b', colorValues1b); - const legend = computeLegend(seriesColor, seriesColorMap, specs, 'violet', axesSpecs); + seriesCollection.set('seriesCollectionValue1a', seriesCollectionValue1a); + seriesCollection.set('seriesCollectionValue1b', seriesCollectionValue1b); + const legend = computeLegend(seriesCollection, seriesCollectionMap, specs, 'violet', axesSpecs); const expected = [ { color: 'red', label: 'Spec 1 title', - value: { colorValues: [], specId: 'spec1' }, + seriesIdentifier: { + seriesKeys: ['y1'], + specId: 'spec1', + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'seriesCollectionValue1a', + }, isSeriesVisible: true, isLegendItemVisible: true, - key: 'colorSeries1a', + key: 'seriesCollectionValue1a', displayValue: nullDisplayValue, + banded: undefined, }, { color: 'blue', label: 'a - b', - value: { colorValues: ['a', 'b'], specId: 'spec1' }, + seriesIdentifier: { + seriesKeys: ['a', 'b', 'y1'], + specId: 'spec1', + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'seriesCollectionValue1b', + }, isSeriesVisible: true, isLegendItemVisible: true, - key: 'colorSeries1b', + key: 'seriesCollectionValue1b', displayValue: nullDisplayValue, + banded: undefined, }, ]; expect(Array.from(legend.values())).toEqual(expected); }); it('compute legend for multiple specs', () => { - seriesColor.set('colorSeries1a', colorValues1a); - seriesColor.set('colorSeries2a', colorValues2a); - const legend = computeLegend(seriesColor, seriesColorMap, specs, 'violet', axesSpecs); + seriesCollection.set('seriesCollectionValue1a', seriesCollectionValue1a); + seriesCollection.set('seriesCollectionValue2a', seriesCollectionValue2a); + const legend = computeLegend(seriesCollection, seriesCollectionMap, specs, 'violet', axesSpecs); const expected = [ { color: 'red', label: 'Spec 1 title', - value: { colorValues: [], specId: 'spec1' }, + seriesIdentifier: { + seriesKeys: ['y1'], + specId: 'spec1', + splitAccessors: new Map(), + yAccessor: 'y1', + key: 'seriesCollectionValue1a', + }, isSeriesVisible: true, isLegendItemVisible: true, - key: 'colorSeries1a', + key: 'seriesCollectionValue1a', displayValue: nullDisplayValue, + banded: undefined, }, { color: 'green', label: 'spec2', - value: { colorValues: [], specId: 'spec2' }, + seriesIdentifier: { + seriesKeys: ['y1'], + specId: 'spec2', + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'seriesCollectionValue2a', + }, isSeriesVisible: true, isLegendItemVisible: true, - key: 'colorSeries2a', + key: 'seriesCollectionValue2a', displayValue: nullDisplayValue, + banded: undefined, }, ]; expect(Array.from(legend.values())).toEqual(expected); }); it('empty legend for missing spec', () => { - seriesColor.set('colorSeries2b', colorValues2b); - const legend = computeLegend(seriesColor, seriesColorMap, specs, 'violet', axesSpecs); + seriesCollection.set('seriesCollectionValue2b', seriesCollectionValue2b); + const legend = computeLegend(seriesCollection, seriesCollectionMap, specs, 'violet', axesSpecs); expect(legend.size).toEqual(0); }); it('compute legend with default color for missing series color', () => { - seriesColor.set('colorSeries1a', colorValues1a); + seriesCollection.set('seriesCollectionValue1a', seriesCollectionValue1a); const emptyColorMap = new Map(); - const legend = computeLegend(seriesColor, emptyColorMap, specs, 'violet', axesSpecs); + const legend = computeLegend(seriesCollection, emptyColorMap, specs, 'violet', axesSpecs); const expected = [ { color: 'violet', label: 'Spec 1 title', - value: { colorValues: [], specId: 'spec1' }, + banded: undefined, + seriesIdentifier: { + seriesKeys: ['y1'], + specId: 'spec1', + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'seriesCollectionValue1a', + }, isSeriesVisible: true, isLegendItemVisible: true, - key: 'colorSeries1a', + key: 'seriesCollectionValue1a', displayValue: nullDisplayValue, }, ]; expect(Array.from(legend.values())).toEqual(expected); }); it('default all series legend items to visible when deselectedDataSeries is null', () => { - seriesColor.set('colorSeries1a', colorValues1a); - seriesColor.set('colorSeries1b', colorValues1b); - seriesColor.set('colorSeries2a', colorValues2a); - seriesColor.set('colorSeries2b', colorValues2b); + seriesCollection.set('seriesCollectionValue1a', seriesCollectionValue1a); + seriesCollection.set('seriesCollectionValue1b', seriesCollectionValue1b); + seriesCollection.set('seriesCollectionValue2a', seriesCollectionValue2a); + seriesCollection.set('seriesCollectionValue2b', seriesCollectionValue2b); const emptyColorMap = new Map(); - const legend = computeLegend(seriesColor, emptyColorMap, specs, 'violet', axesSpecs); + const legend = computeLegend(seriesCollection, emptyColorMap, specs, 'violet', axesSpecs); const visibility = [...legend.values()].map((item) => item.isSeriesVisible); expect(visibility).toEqual([true, true, true]); }); it('selectively sets series to visible when there are deselectedDataSeries items', () => { - seriesColor.set('colorSeries1a', colorValues1a); - seriesColor.set('colorSeries1b', colorValues1b); - seriesColor.set('colorSeries2a', colorValues2a); - seriesColor.set('colorSeries2b', colorValues2b); + seriesCollection.set('seriesCollectionValue1a', seriesCollectionValue1a); + seriesCollection.set('seriesCollectionValue1b', seriesCollectionValue1b); + seriesCollection.set('seriesCollectionValue2a', seriesCollectionValue2a); + seriesCollection.set('seriesCollectionValue2b', seriesCollectionValue2b); const emptyColorMap = new Map(); - const deselectedDataSeries = [colorValues1a, colorValues1b]; + const deselectedDataSeries = [seriesCollectionValue1a.seriesIdentifier, seriesCollectionValue1b.seriesIdentifier]; - const legend = computeLegend(seriesColor, emptyColorMap, specs, 'violet', axesSpecs, deselectedDataSeries); + const legend = computeLegend(seriesCollection, emptyColorMap, specs, 'violet', axesSpecs, deselectedDataSeries); const visibility = [...legend.values()].map((item) => item.isSeriesVisible); expect(visibility).toEqual([false, false, true]); }); it('returns the right series label for a color series', () => { - let label = getSeriesColorLabel([], true); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([], true, spec1); + const seriesIdentifier1 = { + specId: getSpecId(''), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['y1'], + key: '', + }; + const seriesIdentifier2 = { + specId: getSpecId(''), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'y1'], + key: '', + }; + + // null removed, seriesIdentifier has to be at least an empty array + let label = getSeriesLabel(seriesIdentifier1, true, false); + expect(label).toBe(''); + label = getSeriesLabel(seriesIdentifier1, true, false, spec1); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([], true, spec2); + label = getSeriesLabel(seriesIdentifier1, true, false, spec2); expect(label).toBe('spec2'); - label = getSeriesColorLabel(['a', 'b'], true, spec1); + label = getSeriesLabel(seriesIdentifier2, true, false, spec1); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel(['a', 'b'], true, spec2); + label = getSeriesLabel(seriesIdentifier2, true, false, spec2); expect(label).toBe('spec2'); - label = getSeriesColorLabel([], false); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([], false, spec1); + label = getSeriesLabel(seriesIdentifier1, false, false, spec1); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([], false, spec2); + label = getSeriesLabel(seriesIdentifier1, false, false, spec2); expect(label).toBe('spec2'); - label = getSeriesColorLabel(['a', 'b'], false, spec1); + label = getSeriesLabel(seriesIdentifier2, false, false, spec1); expect(label).toBe('a - b'); - label = getSeriesColorLabel(['a', 'b'], false, spec2); + label = getSeriesLabel(seriesIdentifier2, false, false, spec2); expect(label).toBe('a - b'); - label = getSeriesColorLabel([null], true); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([null], true, spec1); + label = getSeriesLabel(seriesIdentifier1, true, false, spec1); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([null], true, spec2); + label = getSeriesLabel(seriesIdentifier1, true, false, spec2); expect(label).toBe('spec2'); - label = getSeriesColorLabel([undefined], true); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([undefined], true, spec1); + label = getSeriesLabel(seriesIdentifier1, true, false); + expect(label).toBe(''); + label = getSeriesLabel(seriesIdentifier1, true, false, spec1); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([undefined], true, spec2); + label = getSeriesLabel(seriesIdentifier1, true, false, spec2); expect(label).toBe('spec2'); - - label = getSeriesColorLabel([0], true); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([0], true, spec1); - expect(label).toBe('Spec 1 title'); - - label = getSeriesColorLabel([null], false); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([null], false, spec1); - expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([null], false, spec2); - expect(label).toBe('spec2'); - label = getSeriesColorLabel([undefined], false); - expect(label).toBeUndefined(); - label = getSeriesColorLabel([undefined], false, spec1); - expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([undefined], false, spec2); - expect(label).toBe('spec2'); - - label = getSeriesColorLabel([0], false); - expect(label).toBe('0'); - label = getSeriesColorLabel([0], false, spec1); - expect(label).toBe('0'); }); it('use the splitted value as label if has a single series and splitSeries is used', () => { + const seriesIdentifier1 = { + specId: getSpecId(''), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['y1'], + key: '', + }; + const seriesIdentifier2 = { + specId: getSpecId(''), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'y1'], + key: '', + }; + const seriesIdentifier3 = { + specId: getSpecId(''), + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'y1'], + key: '', + }; + const specWithSplit: BasicSeriesSpec = { ...spec1, splitSeriesAccessors: ['g'], }; - let label = getSeriesColorLabel([], true, specWithSplit); + let label = getSeriesLabel(seriesIdentifier1, true, false, specWithSplit); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel(['a'], true, specWithSplit); + label = getSeriesLabel(seriesIdentifier3, true, false, specWithSplit); expect(label).toBe('a'); // happens when we have multiple values in splitSeriesAccessor // or we have also multiple yAccessors - label = getSeriesColorLabel(['a', 'b'], true, specWithSplit); + label = getSeriesLabel(seriesIdentifier2, true, false, specWithSplit); expect(label).toBe('a - b'); // happens when the value of a splitSeriesAccessor is null - label = getSeriesColorLabel([null], true, specWithSplit); + label = getSeriesLabel(seriesIdentifier1, true, false, specWithSplit); expect(label).toBe('Spec 1 title'); - label = getSeriesColorLabel([], false, specWithSplit); + label = getSeriesLabel(seriesIdentifier1, false, false, specWithSplit); expect(label).toBe('Spec 1 title'); }); }); diff --git a/src/chart_types/xy_chart/legend/legend.ts b/src/chart_types/xy_chart/legend/legend.ts index 48ede4e62c..42f4df4cf8 100644 --- a/src/chart_types/xy_chart/legend/legend.ts +++ b/src/chart_types/xy_chart/legend/legend.ts @@ -1,13 +1,15 @@ import { getAxesSpecForSpecId, LastValues, getSpecsById } from '../state/utils'; import { identity } from '../../../utils/commons'; import { - DataSeriesColorsValues, + SeriesCollectionValue, + getSeriesIndex, getSortedDataSeriesColorsValuesMap, - findDataSeriesByColorValues, + SeriesIdentifier, + getSeriesLabel, } from '../utils/series'; import { AxisSpec, BasicSeriesSpec, Postfixes, isAreaSeriesSpec, isBarSeriesSpec } from '../utils/specs'; import { Y0_ACCESSOR_POSTFIX, Y1_ACCESSOR_POSTFIX } from '../tooltip/tooltip'; -import { AccessorType } from '../../../utils/geometry'; +import { BandedAccessorType } from '../../../utils/geometry'; interface FormattedLastValues { y0: number | string | null; @@ -18,7 +20,7 @@ export type LegendItem = Postfixes & { key: string; color: string; label: string; - value: DataSeriesColorsValues; + seriesIdentifier: SeriesIdentifier; isSeriesVisible?: boolean; banded?: boolean; isLegendItemVisible?: boolean; @@ -42,35 +44,35 @@ function getPostfix(spec: BasicSeriesSpec): Postfixes { export function getItemLabel( { banded, label, y1AccessorFormat, y0AccessorFormat }: LegendItem, - yAccessor: AccessorType, + yAccessor: BandedAccessorType, ) { if (!banded) { return label; } - return yAccessor === AccessorType.Y1 ? `${label}${y1AccessorFormat}` : `${label}${y0AccessorFormat}`; + return yAccessor === BandedAccessorType.Y1 ? `${label}${y1AccessorFormat}` : `${label}${y0AccessorFormat}`; } export function computeLegend( - seriesColor: Map, - seriesColorMap: Map, + seriesCollection: Map, + seriesColors: Map, specs: BasicSeriesSpec[], defaultColor: string, axesSpecs: AxisSpec[], - deselectedDataSeries: DataSeriesColorsValues[] = [], + deselectedDataSeries: SeriesIdentifier[] = [], ): Map { const legendItems: Map = new Map(); - const sortedSeriesColors = getSortedDataSeriesColorsValuesMap(seriesColor); + const sortedCollection = getSortedDataSeriesColorsValuesMap(seriesCollection); - sortedSeriesColors.forEach((series, key) => { - const { banded, specId, lastValue, colorValues } = series; - const spec = getSpecsById(specs, specId); - const color = seriesColorMap.get(key) || defaultColor; - const hasSingleSeries = seriesColor.size === 1; - const label = getSeriesColorLabel(colorValues, hasSingleSeries, spec); - const isSeriesVisible = deselectedDataSeries ? findDataSeriesByColorValues(deselectedDataSeries, series) < 0 : true; + sortedCollection.forEach((series, key) => { + const { banded, lastValue, seriesIdentifier } = series; + const spec = getSpecsById(specs, seriesIdentifier.specId); + const color = seriesColors.get(key) || defaultColor; + const hasSingleSeries = seriesCollection.size === 1; + const label = getSeriesLabel(seriesIdentifier, hasSingleSeries, false, spec); + const isSeriesVisible = deselectedDataSeries ? getSeriesIndex(deselectedDataSeries, seriesIdentifier) < 0 : true; - if (!label || !spec) { + if (label === '' || !spec) { return; } @@ -84,7 +86,7 @@ export function computeLegend( color, label, banded, - value: series, + seriesIdentifier, isSeriesVisible, isLegendItemVisible: !hideInLegend, displayValue: { @@ -104,25 +106,3 @@ export function computeLegend( }); return legendItems; } - -export function getSeriesColorLabel( - colorValues: Array, - hasSingleSeries: boolean, - spec?: BasicSeriesSpec, -): string | undefined { - let label = ''; - if (hasSingleSeries || colorValues.length === 0 || colorValues[0] == null) { - if (!spec) { - return; - } - if (spec.splitSeriesAccessors && colorValues.length > 0 && colorValues[0] !== null) { - label = colorValues.join(' - '); - } else { - label = spec.name || `${spec.id}`; - } - } else { - label = colorValues.join(' - '); - } - - return label; -} diff --git a/src/chart_types/xy_chart/renderer/canvas/area_geometries.tsx b/src/chart_types/xy_chart/renderer/canvas/area_geometries.tsx index e5d0f0bb24..0bed4c6488 100644 --- a/src/chart_types/xy_chart/renderer/canvas/area_geometries.tsx +++ b/src/chart_types/xy_chart/renderer/canvas/area_geometries.tsx @@ -8,12 +8,13 @@ import { PointStyleProps, buildLineRenderProps, } from './utils/rendering_props_utils'; -import { getGeometryIdKey, getGeometryStateStyle } from '../../rendering/rendering'; +import { getSeriesIdentifierPrefixedKey, getGeometryStateStyle } from '../../rendering/rendering'; import { mergePartial } from '../../../../utils/commons'; -import { AreaGeometry, PointGeometry, GeometryId } from '../../../../utils/geometry'; +import { AreaGeometry, PointGeometry } from '../../../../utils/geometry'; import { PointStyle, SharedGeometryStateStyle } from '../../../../utils/themes/theme'; import { LegendItem } from '../../legend/legend'; import { Clippings, clipRanges } from './bar_values_utils'; +import { SeriesIdentifier } from '../../utils/series'; interface AreaGeometriesDataProps { animated?: boolean; @@ -47,7 +48,7 @@ export class AreaGeometries extends React.PureComponent { const { sharedStyle, highlightedLegendItem, areas, clippings } = this.props; return areas.reduce((acc, glyph, i) => { - const { seriesAreaLineStyle, seriesAreaStyle, seriesPointStyle, geometryId } = glyph; + const { seriesAreaLineStyle, seriesAreaStyle, seriesPointStyle, seriesIdentifier } = glyph; if (seriesAreaStyle.visible) { acc.push(this.renderArea(glyph, sharedStyle, highlightedLegendItem, clippings)); } @@ -55,9 +56,13 @@ export class AreaGeometries extends React.PureComponent { - const { area, color, transform, geometryId, seriesAreaStyle, clippedRanges } = glyph; - const geometryStateStyle = getGeometryStateStyle(geometryId, highlightedLegendItem, sharedStyle); - const key = getGeometryIdKey(geometryId, 'area-'); + const { area, color, transform, seriesIdentifier, seriesAreaStyle, clippedRanges } = glyph; + const geometryStateStyle = getGeometryStateStyle(seriesIdentifier, highlightedLegendItem, sharedStyle); + const key = getSeriesIdentifierPrefixedKey(seriesIdentifier, 'area-'); const areaProps = buildAreaRenderProps(transform.x, area, color, seriesAreaStyle, geometryStateStyle); if (clippedRanges.length > 0) { @@ -99,11 +104,11 @@ export class AreaGeometries extends React.PureComponent { - const { lines, color, geometryId, transform, seriesAreaLineStyle, clippedRanges } = glyph; - const geometryStateStyle = getGeometryStateStyle(geometryId, highlightedLegendItem, sharedStyle); - const groupKey = getGeometryIdKey(geometryId, `area-line-${areaIndex}`); + const { lines, color, seriesIdentifier, transform, seriesAreaLineStyle, clippedRanges } = glyph; + const geometryStateStyle = getGeometryStateStyle(seriesIdentifier, highlightedLegendItem, sharedStyle); + const groupKey = getSeriesIdentifierPrefixedKey(seriesIdentifier, `area-line-${areaIndex}`); const linesElementProps = lines.map<{ key: string; props: PathConfig }>((linePath, lineIndex) => { - const key = getGeometryIdKey(geometryId, `area-line-${areaIndex}-${lineIndex}`); + const key = getSeriesIdentifierPrefixedKey(seriesIdentifier, `area-line-${areaIndex}-${lineIndex}`); const props = buildLineRenderProps(transform.x, linePath, color, seriesAreaLineStyle, geometryStateStyle); return { key, props }; }); @@ -146,11 +151,11 @@ export class AreaGeometries extends React.PureComponent { return areaPoints.map((areaPoint, pointIndex) => { const { x, y, transform, styleOverrides } = areaPoint; - const key = getGeometryIdKey(geometryId, `area-point-${areaIndex}-${pointIndex}-`); + const key = getSeriesIdentifierPrefixedKey(seriesIdentifier, `area-point-${areaIndex}-${pointIndex}-`); const pointStyle = this.mergePointPropsWithOverrides(pointStyleProps, styleOverrides); const pointProps = buildPointRenderProps(transform.x + x, y, pointStyle); return ; diff --git a/src/chart_types/xy_chart/renderer/canvas/bar_geometries.tsx b/src/chart_types/xy_chart/renderer/canvas/bar_geometries.tsx index 4fa7cd11c7..c38f4c3b27 100644 --- a/src/chart_types/xy_chart/renderer/canvas/bar_geometries.tsx +++ b/src/chart_types/xy_chart/renderer/canvas/bar_geometries.tsx @@ -55,7 +55,7 @@ export class BarGeometries extends React.PureComponent((acc, line) => { - const { seriesLineStyle, seriesPointStyle, geometryId } = line; - const key = getGeometryIdKey(geometryId, 'line-'); + const { seriesLineStyle, seriesPointStyle, seriesIdentifier } = line; + const key = getSeriesIdentifierPrefixedKey(seriesIdentifier, 'line-'); if (seriesLineStyle.visible) { acc.push(this.getLineToRender(line, sharedStyle, key)); } @@ -89,8 +89,9 @@ export class LineGeometries extends React.PureComponent 0) { @@ -114,8 +115,8 @@ export class LineGeometries extends React.PureComponent {
{this.renderHeader(tooltipValues[0], tooltipHeaderFormatter)}
- {tooltipValues.slice(1).map(({ name, value, color, isHighlighted, seriesKey, yAccessor }) => { + {tooltipValues.slice(1).map(({ name, value, color, isHighlighted, seriesKey, yAccessor, isVisible }) => { + if (!isVisible) { + return null; + } const classes = classNames('echTooltip__item', { /* eslint @typescript-eslint/camelcase:0 */ echTooltip__rowHighlighted: isHighlighted, diff --git a/src/chart_types/xy_chart/rendering/rendering.areas.test.ts b/src/chart_types/xy_chart/rendering/rendering.areas.test.ts index 367266ec8b..ec2736ea9c 100644 --- a/src/chart_types/xy_chart/rendering/rendering.areas.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.areas.test.ts @@ -45,27 +45,25 @@ describe('Rendering points - areas', () => { beforeEach(() => { renderedArea = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - [], + { ...pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], data: [] }, xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); }); test('Render geometry but empty upper and lower lines and area paths', () => { const { - areaGeometry: { lines, area, color, geometryId, transform }, + areaGeometry: { lines, area, color, seriesIdentifier, transform }, } = renderedArea; expect(lines.length).toBe(0); expect(area).toBe(''); expect(color).toBe('red'); - expect(geometryId.seriesKey).toEqual([]); - expect(geometryId.specId).toEqual(SPEC_ID); + expect(seriesIdentifier.seriesKeys).toEqual([1]); + expect(seriesIdentifier.specId).toEqual(SPEC_ID); expect(transform).toEqual({ x: 25, y: 0 }); }); }); @@ -99,27 +97,25 @@ describe('Rendering points - areas', () => { beforeEach(() => { renderedArea = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); }); test('Can render an line and area paths', () => { const { - areaGeometry: { lines, area, color, geometryId, transform }, + areaGeometry: { lines, area, color, seriesIdentifier, transform }, } = renderedArea; expect(lines[0]).toBe('M0,0L50,50'); expect(area).toBe('M0,0L50,50L50,100L0,100Z'); expect(color).toBe('red'); - expect(geometryId.seriesKey).toEqual([]); - expect(geometryId.specId).toEqual(SPEC_ID); + expect(seriesIdentifier.seriesKeys).toEqual([1]); + expect(seriesIdentifier.specId).toEqual(SPEC_ID); expect(transform).toEqual({ x: 25, y: 0 }); }); @@ -129,35 +125,42 @@ describe('Rendering points - areas', () => { indexedGeometries, } = renderedArea; - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, y: 10, }, - transform: { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -167,13 +170,13 @@ describe('Rendering points - areas', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); describe('Multi series area chart - ordinal', () => { - const spec1Id = 'point1'; - const spec2Id = 'point2'; + const spec1Id = 'spec_1'; + const spec2Id = 'spec_2'; const pointSeriesSpec1: AreaSeriesSpec = { chartType: ChartTypes.XYAxis, specType: SpecTypes.Series, @@ -221,27 +224,23 @@ describe('Rendering points - areas', () => { beforeEach(() => { firstLine = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - spec1Id, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); secondLine = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', CurveType.LINEAR, - spec2Id, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); @@ -251,15 +250,15 @@ describe('Rendering points - areas', () => { expect(firstLine.areaGeometry.lines[0]).toBe('M0,50L50,75'); expect(firstLine.areaGeometry.area).toBe('M0,50L50,75L50,100L0,100Z'); expect(firstLine.areaGeometry.color).toBe('red'); - expect(firstLine.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(firstLine.areaGeometry.geometryId.specId).toEqual(spec1Id); + expect(firstLine.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(firstLine.areaGeometry.seriesIdentifier.specId).toEqual(spec1Id); expect(firstLine.areaGeometry.transform).toEqual({ x: 25, y: 0 }); expect(secondLine.areaGeometry.lines[0]).toBe('M0,0L50,50'); expect(secondLine.areaGeometry.area).toBe('M0,0L50,50L50,100L0,100Z'); expect(secondLine.areaGeometry.color).toBe('blue'); - expect(secondLine.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(secondLine.areaGeometry.geometryId.specId).toEqual(spec2Id); + expect(secondLine.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(secondLine.areaGeometry.seriesIdentifier.specId).toEqual(spec2Id); expect(secondLine.areaGeometry.transform).toEqual({ x: 25, y: 0 }); }); test('can render first spec points', () => { @@ -268,15 +267,19 @@ describe('Rendering points - areas', () => { indexedGeometries, } = firstLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, @@ -286,16 +289,20 @@ describe('Rendering points - areas', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 75, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -305,7 +312,7 @@ describe('Rendering points - areas', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); test('can render second spec points', () => { @@ -314,15 +321,19 @@ describe('Rendering points - areas', () => { indexedGeometries, } = secondLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_2}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, @@ -332,16 +343,20 @@ describe('Rendering points - areas', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 50, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_2}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -351,7 +366,7 @@ describe('Rendering points - areas', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -386,14 +401,12 @@ describe('Rendering points - areas', () => { beforeEach(() => { renderedArea = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); @@ -402,8 +415,8 @@ describe('Rendering points - areas', () => { expect(renderedArea.areaGeometry.lines[0]).toBe('M0,0L100,50'); expect(renderedArea.areaGeometry.area).toBe('M0,0L100,50L100,100L0,100Z'); expect(renderedArea.areaGeometry.color).toBe('red'); - expect(renderedArea.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(renderedArea.areaGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(renderedArea.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(renderedArea.areaGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(renderedArea.areaGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('Can render two points', () => { @@ -411,15 +424,19 @@ describe('Rendering points - areas', () => { areaGeometry: { points }, indexedGeometries, } = renderedArea; - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, @@ -429,16 +446,20 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -448,13 +469,13 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); describe('Multi series area chart - linear', () => { - const spec1Id = 'point1'; - const spec2Id = 'point2'; + const spec1Id = 'spec_1'; + const spec2Id = 'spec_2'; const pointSeriesSpec1: AreaSeriesSpec = { chartType: ChartTypes.XYAxis, specType: SpecTypes.Series, @@ -502,27 +523,23 @@ describe('Rendering points - areas', () => { beforeEach(() => { firstLine = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - spec1Id, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); secondLine = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', CurveType.LINEAR, - spec2Id, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); @@ -531,15 +548,15 @@ describe('Rendering points - areas', () => { expect(firstLine.areaGeometry.lines[0]).toBe('M0,50L100,75'); expect(firstLine.areaGeometry.area).toBe('M0,50L100,75L100,100L0,100Z'); expect(firstLine.areaGeometry.color).toBe('red'); - expect(firstLine.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(firstLine.areaGeometry.geometryId.specId).toEqual(spec1Id); + expect(firstLine.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(firstLine.areaGeometry.seriesIdentifier.specId).toEqual(spec1Id); expect(firstLine.areaGeometry.transform).toEqual({ x: 0, y: 0 }); expect(secondLine.areaGeometry.lines[0]).toBe('M0,0L100,50'); expect(secondLine.areaGeometry.area).toBe('M0,0L100,50L100,100L0,100Z'); expect(secondLine.areaGeometry.color).toBe('blue'); - expect(secondLine.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(secondLine.areaGeometry.geometryId.specId).toEqual(spec2Id); + expect(secondLine.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(secondLine.areaGeometry.seriesIdentifier.specId).toEqual(spec2Id); expect(secondLine.areaGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('can render first spec points', () => { @@ -548,15 +565,19 @@ describe('Rendering points - areas', () => { indexedGeometries, } = firstLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, @@ -566,16 +587,20 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 75, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -585,7 +610,7 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); test('can render second spec points', () => { @@ -594,15 +619,19 @@ describe('Rendering points - areas', () => { indexedGeometries, } = secondLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_2}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, @@ -612,16 +641,20 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_2}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -631,7 +664,7 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -666,14 +699,12 @@ describe('Rendering points - areas', () => { beforeEach(() => { renderedArea = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); @@ -682,8 +713,8 @@ describe('Rendering points - areas', () => { expect(renderedArea.areaGeometry.lines[0]).toBe('M0,0L100,50'); expect(renderedArea.areaGeometry.area).toBe('M0,0L100,50L100,100L0,100Z'); expect(renderedArea.areaGeometry.color).toBe('red'); - expect(renderedArea.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(renderedArea.areaGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(renderedArea.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(renderedArea.areaGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(renderedArea.areaGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('Can render two points', () => { @@ -691,15 +722,19 @@ describe('Rendering points - areas', () => { areaGeometry: { points }, indexedGeometries, } = renderedArea; - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1546300800000, @@ -709,16 +744,20 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1546387200000, @@ -728,13 +767,13 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); describe('Multi series area chart - time', () => { - const spec1Id = 'point1'; - const spec2Id = 'point2'; + const spec1Id = 'spec_1'; + const spec2Id = 'spec_2'; const pointSeriesSpec1: AreaSeriesSpec = { chartType: ChartTypes.XYAxis, specType: SpecTypes.Series, @@ -782,27 +821,23 @@ describe('Rendering points - areas', () => { beforeEach(() => { firstLine = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - spec1Id, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); secondLine = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', CurveType.LINEAR, - spec2Id, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); @@ -813,15 +848,19 @@ describe('Rendering points - areas', () => { indexedGeometries, } = firstLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1546300800000, @@ -831,16 +870,20 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 75, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1546387200000, @@ -850,7 +893,7 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); test('can render second spec points', () => { @@ -859,15 +902,19 @@ describe('Rendering points - areas', () => { indexedGeometries, } = secondLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_2}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1546300800000, @@ -877,16 +924,20 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], + key: 'spec{spec_2}yAccessor{1}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1546387200000, @@ -896,7 +947,7 @@ describe('Rendering points - areas', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -931,14 +982,12 @@ describe('Rendering points - areas', () => { beforeEach(() => { renderedArea = renderArea( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.areaSeriesStyle, ); @@ -948,8 +997,8 @@ describe('Rendering points - areas', () => { expect(renderedArea.areaGeometry.lines[0].split('M').length - 1).toBe(3); expect(renderedArea.areaGeometry.area.split('M').length - 1).toBe(3); expect(renderedArea.areaGeometry.color).toBe('red'); - expect(renderedArea.areaGeometry.geometryId.seriesKey).toEqual([]); - expect(renderedArea.areaGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(renderedArea.areaGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(renderedArea.areaGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(renderedArea.areaGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('Can render points points', () => { diff --git a/src/chart_types/xy_chart/rendering/rendering.bands.test.ts b/src/chart_types/xy_chart/rendering/rendering.bands.test.ts index 42a19c4441..d7036467a7 100644 --- a/src/chart_types/xy_chart/rendering/rendering.bands.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.bands.test.ts @@ -43,27 +43,25 @@ describe('Rendering bands - areas', () => { beforeEach(() => { renderedArea = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - [], + { ...pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], data: [] }, xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, true, - [], 0, LIGHT_THEME.areaSeriesStyle, ); }); test('Render geometry but empty upper and lower lines and area paths', () => { const { - areaGeometry: { lines, area, color, geometryId, transform }, + areaGeometry: { lines, area, color, seriesIdentifier, transform }, } = renderedArea; expect(lines.length).toBe(0); expect(area).toBe(''); expect(color).toBe('red'); - expect(geometryId.seriesKey).toEqual([]); - expect(geometryId.specId).toEqual(SPEC_ID); + expect(seriesIdentifier.seriesKeys).toEqual([2]); + expect(seriesIdentifier.specId).toEqual(SPEC_ID); expect(transform).toEqual({ x: 25, y: 0 }); }); }); @@ -98,29 +96,27 @@ describe('Rendering bands - areas', () => { beforeEach(() => { renderedArea = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, true, - [], 0, LIGHT_THEME.areaSeriesStyle, ); }); test('Can render upper and lower lines and area paths', () => { const { - areaGeometry: { lines, area, color, geometryId, transform }, + areaGeometry: { lines, area, color, seriesIdentifier, transform }, } = renderedArea; expect(lines.length).toBe(2); expect(lines[0]).toBe('M0,0L50,50'); expect(lines[1]).toBe('M0,80L50,70'); expect(area).toBe('M0,0L50,50L50,70L0,80Z'); expect(color).toBe('red'); - expect(geometryId.seriesKey).toEqual([]); - expect(geometryId.specId).toEqual(SPEC_ID); + expect(seriesIdentifier.seriesKeys).toEqual([2]); + expect(seriesIdentifier.specId).toEqual(SPEC_ID); expect(transform).toEqual({ x: 25, y: 0 }); }); @@ -129,75 +125,89 @@ describe('Rendering bands - areas', () => { areaGeometry: { points }, } = renderedArea; expect(points.length).toBe(4); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 80, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y0', x: 0, y: 2, }, - transform: { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); - expect(points[1]).toEqual({ + expect(points[1]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, y: 10, }, - transform: { x: 25, y: 0, }, - } as PointGeometry); - expect(points[2]).toEqual({ + } as unknown) as PointGeometry); + expect(points[2]).toEqual(({ x: 50, y: 70, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y0', x: 1, y: 3, }, + styleOverrides: undefined, transform: { x: 25, y: 0, }, - } as PointGeometry); - expect(points[3]).toEqual({ + } as unknown) as PointGeometry); + expect(points[3]).toEqual(({ x: 50, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, + styleOverrides: undefined, value: { accessor: 'y1', x: 1, @@ -207,7 +217,7 @@ describe('Rendering bands - areas', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); }); }); describe('Single band area chart with null values', () => { @@ -241,29 +251,27 @@ describe('Rendering bands - areas', () => { beforeEach(() => { renderedArea = renderArea( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, true, - [], 0, LIGHT_THEME.areaSeriesStyle, ); }); test('Can render upper and lower lines and area paths', () => { const { - areaGeometry: { lines, area, color, geometryId, transform }, + areaGeometry: { lines, area, color, seriesIdentifier, transform }, } = renderedArea; expect(lines.length).toBe(2); expect(lines[0]).toBe('M0,0ZM50,50L75,50'); expect(lines[1]).toBe('M0,80ZM50,70L75,70'); expect(area).toBe('M0,0L0,80ZM50,50L75,50L75,70L50,70Z'); expect(color).toBe('red'); - expect(geometryId.seriesKey).toEqual([]); - expect(geometryId.specId).toEqual(SPEC_ID); + expect(seriesIdentifier.seriesKeys).toEqual([2]); + expect(seriesIdentifier.specId).toEqual(SPEC_ID); expect(transform).toEqual({ x: 25, y: 0 }); }); @@ -273,55 +281,62 @@ describe('Rendering bands - areas', () => { } = renderedArea; // expect(points).toBe(6); expect(points.length).toBe(6); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 80, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y0', x: 0, y: 2, }, - transform: { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); - expect(points[1]).toEqual({ + expect(points[1]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y1', x: 0, y: 10, }, - transform: { x: 25, y: 0, }, - } as PointGeometry); - expect(points[2]).toEqual({ + } as unknown) as PointGeometry); + expect(points[2]).toEqual(({ x: 50, y: 70, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y0', @@ -332,15 +347,18 @@ describe('Rendering bands - areas', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[3]).toEqual({ + } as unknown) as PointGeometry); + expect(points[3]).toEqual(({ x: 50, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y1', @@ -351,15 +369,18 @@ describe('Rendering bands - areas', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[4]).toEqual({ + } as unknown) as PointGeometry); + expect(points[4]).toEqual(({ x: 75, y: 70, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y0', @@ -370,15 +391,18 @@ describe('Rendering bands - areas', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[5]).toEqual({ + } as unknown) as PointGeometry); + expect(points[5]).toEqual(({ x: 75, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, value: { accessor: 'y1', @@ -389,7 +413,7 @@ describe('Rendering bands - areas', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); }); }); describe('Single series band bar chart - ordinal', () => { @@ -419,12 +443,10 @@ describe('Rendering bands - areas', () => { test('Can render two bars', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toBe(3); @@ -439,9 +461,12 @@ describe('Rendering bands - areas', () => { x: 0, y: 10, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, displayValue: undefined, seriesStyle: { @@ -474,9 +499,12 @@ describe('Rendering bands - areas', () => { x: 2, y: 5, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, displayValue: undefined, seriesStyle: { @@ -509,9 +537,12 @@ describe('Rendering bands - areas', () => { x: 3, y: 8, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + yAccessor: 2, + splitAccessors: new Map(), + seriesKeys: [2], + key: 'spec{spec_1}yAccessor{2}splitAccessors{}', }, displayValue: undefined, seriesStyle: { diff --git a/src/chart_types/xy_chart/rendering/rendering.bars.test.ts b/src/chart_types/xy_chart/rendering/rendering.bars.test.ts index ed3d915f75..4032752088 100644 --- a/src/chart_types/xy_chart/rendering/rendering.bars.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.bars.test.ts @@ -39,12 +39,10 @@ describe('Rendering bars', () => { test('Can render two bars within domain', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, ); @@ -59,9 +57,12 @@ describe('Rendering bars', () => { x: 0, y: 10, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -94,9 +95,12 @@ describe('Rendering bars', () => { x: 1, y: 5, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -124,12 +128,10 @@ describe('Rendering bars', () => { const valueFormatter = identity; const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, { valueFormatter, showValueLabel: true, isAlternatingValueLabel: true }, ); @@ -139,12 +141,10 @@ describe('Rendering bars', () => { test('Can hide value labels if no formatter or showValueLabels is false/undefined', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, {}, ); @@ -155,12 +155,10 @@ describe('Rendering bars', () => { const valueFormatter = identity; const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, { valueFormatter, showValueLabel: true, isAlternatingValueLabel: true }, ); @@ -172,12 +170,10 @@ describe('Rendering bars', () => { const valueFormatter = identity; const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, { valueFormatter, showValueLabel: true, isValueContainedInElement: true }, ); @@ -225,12 +221,10 @@ describe('Rendering bars', () => { test('can render first spec bars', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - spec1Id, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toEqual(2); @@ -245,9 +239,12 @@ describe('Rendering bars', () => { x: 0, y: 10, }, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{bar1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -280,9 +277,12 @@ describe('Rendering bars', () => { x: 1, y: 5, }, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{bar1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -308,12 +308,10 @@ describe('Rendering bars', () => { test('can render second spec bars', () => { const { barGeometries } = renderBars( 1, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', - spec2Id, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toEqual(2); @@ -328,9 +326,12 @@ describe('Rendering bars', () => { x: 0, y: 20, }, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{bar2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -363,9 +364,12 @@ describe('Rendering bars', () => { x: 1, y: 10, }, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{bar2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -415,12 +419,10 @@ describe('Rendering bars', () => { test('Can render two bars', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries[0]).toEqual({ @@ -434,9 +436,12 @@ describe('Rendering bars', () => { x: 0, y: 10, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -469,9 +474,12 @@ describe('Rendering bars', () => { x: 1, y: 5, }, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -520,12 +528,10 @@ describe('Rendering bars', () => { test('Can render correct bar height', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toBe(6); @@ -576,12 +582,10 @@ describe('Rendering bars', () => { test('can render first spec bars', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - spec1Id, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toEqual(2); @@ -596,9 +600,12 @@ describe('Rendering bars', () => { x: 0, y: 10, }, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{bar1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -631,9 +638,12 @@ describe('Rendering bars', () => { x: 1, y: 5, }, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{bar1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -659,12 +669,10 @@ describe('Rendering bars', () => { test('can render second spec bars', () => { const { barGeometries } = renderBars( 1, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', - spec2Id, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toEqual(2); @@ -679,9 +687,12 @@ describe('Rendering bars', () => { x: 0, y: 20, }, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{bar2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -714,9 +725,12 @@ describe('Rendering bars', () => { x: 1, y: 10, }, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{bar2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -781,12 +795,10 @@ describe('Rendering bars', () => { test('can render first spec bars', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - spec1Id, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toEqual(2); @@ -801,9 +813,12 @@ describe('Rendering bars', () => { x: 1546300800000, y: 10, }, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{bar1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -836,9 +851,12 @@ describe('Rendering bars', () => { x: 1546387200000, y: 5, }, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{bar1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -864,12 +882,10 @@ describe('Rendering bars', () => { test('can render second spec bars', () => { const { barGeometries } = renderBars( 1, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', - spec2Id, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toEqual(2); @@ -884,9 +900,12 @@ describe('Rendering bars', () => { x: 1546300800000, y: 20, }, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{bar2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -919,9 +938,12 @@ describe('Rendering bars', () => { x: 1546387200000, y: 10, }, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{bar2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, displayValue: undefined, seriesStyle: { @@ -976,12 +998,10 @@ describe('Rendering bars', () => { test('Can render 3 bars', () => { const { barGeometries, indexedGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, ); expect(barGeometries.length).toBe(3); @@ -1040,12 +1060,10 @@ describe('Rendering bars', () => { it('should render correct heights with positive minBarHeight', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, undefined, undefined, @@ -1057,12 +1075,10 @@ describe('Rendering bars', () => { it('should render correct heights with negative minBarHeight', () => { const { barGeometries } = renderBars( 0, - barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + barSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', - SPEC_ID, - [], LIGHT_THEME.barSeriesStyle, undefined, undefined, diff --git a/src/chart_types/xy_chart/rendering/rendering.lines.test.ts b/src/chart_types/xy_chart/rendering/rendering.lines.test.ts index 8eb35367fd..85ecc37b8d 100644 --- a/src/chart_types/xy_chart/rendering/rendering.lines.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.lines.test.ts @@ -43,14 +43,12 @@ describe('Rendering points - line', () => { beforeEach(() => { renderedLine = renderLine( 25, // adding a ideal 25px shift, generally applied by renderGeometries - [], + { ...pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], data: [] }, xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -59,8 +57,8 @@ describe('Rendering points - line', () => { const { lineGeometry } = renderedLine; expect(lineGeometry.line).toBe(''); expect(lineGeometry.color).toBe('red'); - expect(lineGeometry.geometryId.seriesKey).toEqual([]); - expect(lineGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(lineGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(lineGeometry.transform).toEqual({ x: 25, y: 0 }); }); }); @@ -94,14 +92,12 @@ describe('Rendering points - line', () => { beforeEach(() => { renderedLine = renderLine( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -110,8 +106,8 @@ describe('Rendering points - line', () => { const { lineGeometry } = renderedLine; expect(lineGeometry.line).toBe('M0,0L50,50'); expect(lineGeometry.color).toBe('red'); - expect(lineGeometry.geometryId.seriesKey).toEqual([]); - expect(lineGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(lineGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(lineGeometry.transform).toEqual({ x: 25, y: 0 }); }); test('Can render two points', () => { @@ -120,15 +116,19 @@ describe('Rendering points - line', () => { indexedGeometries, } = renderedLine; - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, + styleOverrides: undefined, value: { accessor: 'y1', x: 0, @@ -138,15 +138,18 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -157,7 +160,7 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -211,27 +214,23 @@ describe('Rendering points - line', () => { beforeEach(() => { firstLine = renderLine( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - spec1Id, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); secondLine = renderLine( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', CurveType.LINEAR, - spec2Id, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -240,14 +239,14 @@ describe('Rendering points - line', () => { test('Can render two ordinal lines', () => { expect(firstLine.lineGeometry.line).toBe('M0,50L50,75'); expect(firstLine.lineGeometry.color).toBe('red'); - expect(firstLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(firstLine.lineGeometry.geometryId.specId).toEqual(spec1Id); + expect(firstLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(firstLine.lineGeometry.seriesIdentifier.specId).toEqual(spec1Id); expect(firstLine.lineGeometry.transform).toEqual({ x: 25, y: 0 }); expect(secondLine.lineGeometry.line).toBe('M0,0L50,50'); expect(secondLine.lineGeometry.color).toBe('blue'); - expect(secondLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(secondLine.lineGeometry.geometryId.specId).toEqual(spec2Id); + expect(secondLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(secondLine.lineGeometry.seriesIdentifier.specId).toEqual(spec2Id); expect(secondLine.lineGeometry.transform).toEqual({ x: 25, y: 0 }); }); test('can render first spec points', () => { @@ -256,14 +255,17 @@ describe('Rendering points - line', () => { indexedGeometries, } = firstLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{point1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -274,15 +276,18 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 75, color: 'red', radius: 10, - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{point1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -293,7 +298,7 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); test('can render second spec points', () => { @@ -302,14 +307,17 @@ describe('Rendering points - line', () => { indexedGeometries, } = secondLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, color: 'blue', radius: 10, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{point2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -320,15 +328,18 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 50, color: 'blue', radius: 10, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{point2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -339,7 +350,7 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -374,14 +385,12 @@ describe('Rendering points - line', () => { beforeEach(() => { renderedLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -389,8 +398,8 @@ describe('Rendering points - line', () => { test('Can render a linear line', () => { expect(renderedLine.lineGeometry.line).toBe('M0,0L100,50'); expect(renderedLine.lineGeometry.color).toBe('red'); - expect(renderedLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(renderedLine.lineGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(renderedLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(renderedLine.lineGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(renderedLine.lineGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('Can render two points', () => { @@ -398,14 +407,17 @@ describe('Rendering points - line', () => { lineGeometry: { points }, indexedGeometries, } = renderedLine; - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, color: 'red', radius: 10, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -416,15 +428,18 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, color: 'red', radius: 10, - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -435,7 +450,7 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -489,27 +504,23 @@ describe('Rendering points - line', () => { beforeEach(() => { firstLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - spec1Id, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); secondLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', CurveType.LINEAR, - spec2Id, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -517,14 +528,14 @@ describe('Rendering points - line', () => { test('can render two linear lines', () => { expect(firstLine.lineGeometry.line).toBe('M0,50L100,75'); expect(firstLine.lineGeometry.color).toBe('red'); - expect(firstLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(firstLine.lineGeometry.geometryId.specId).toEqual(spec1Id); + expect(firstLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(firstLine.lineGeometry.seriesIdentifier.specId).toEqual(spec1Id); expect(firstLine.lineGeometry.transform).toEqual({ x: 0, y: 0 }); expect(secondLine.lineGeometry.line).toBe('M0,0L100,50'); expect(secondLine.lineGeometry.color).toBe('blue'); - expect(secondLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(secondLine.lineGeometry.geometryId.specId).toEqual(spec2Id); + expect(secondLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(secondLine.lineGeometry.seriesIdentifier.specId).toEqual(spec2Id); expect(secondLine.lineGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('can render first spec points', () => { @@ -533,14 +544,17 @@ describe('Rendering points - line', () => { indexedGeometries, } = firstLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{point1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -551,15 +565,18 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 75, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{point1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -570,7 +587,7 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); test('can render second spec points', () => { @@ -579,14 +596,17 @@ describe('Rendering points - line', () => { indexedGeometries, } = secondLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, color: 'blue', radius: 10, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{point2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -597,15 +617,18 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, color: 'blue', radius: 10, - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{point2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -616,7 +639,7 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -651,14 +674,12 @@ describe('Rendering points - line', () => { beforeEach(() => { renderedLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -666,8 +687,8 @@ describe('Rendering points - line', () => { test('Can render a time line', () => { expect(renderedLine.lineGeometry.line).toBe('M0,0L100,50'); expect(renderedLine.lineGeometry.color).toBe('red'); - expect(renderedLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(renderedLine.lineGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(renderedLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(renderedLine.lineGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(renderedLine.lineGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('Can render two points', () => { @@ -675,14 +696,17 @@ describe('Rendering points - line', () => { lineGeometry: { points }, indexedGeometries, } = renderedLine; - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -693,15 +717,18 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -712,7 +739,7 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -766,27 +793,23 @@ describe('Rendering points - line', () => { beforeEach(() => { firstLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - spec1Id, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); secondLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[1], xScale, yScales.get(GROUP_ID)!, 'blue', CurveType.LINEAR, - spec2Id, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -797,14 +820,17 @@ describe('Rendering points - line', () => { indexedGeometries, } = firstLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 50, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{point1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -815,15 +841,18 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 75, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: spec1Id, - seriesKey: [], + key: 'spec{point1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -834,7 +863,7 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); test('can render second spec points', () => { @@ -843,14 +872,17 @@ describe('Rendering points - line', () => { indexedGeometries, } = secondLine; expect(points.length).toEqual(2); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 0, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{point2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -861,15 +893,18 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 100, y: 50, radius: 10, color: 'blue', - geometryId: { + seriesIdentifier: { specId: spec2Id, - seriesKey: [], + key: 'spec{point2}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -880,7 +915,7 @@ describe('Rendering points - line', () => { x: 0, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); expect(indexedGeometries.size).toEqual(points.length); }); }); @@ -915,14 +950,12 @@ describe('Rendering points - line', () => { beforeEach(() => { renderedLine = renderLine( 0, // not applied any shift, renderGeometries applies it only with mixed charts - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -931,8 +964,8 @@ describe('Rendering points - line', () => { // expect(renderedLine.lineGeometry.line).toBe('ss'); expect(renderedLine.lineGeometry.line.split('M').length - 1).toBe(3); expect(renderedLine.lineGeometry.color).toBe('red'); - expect(renderedLine.lineGeometry.geometryId.seriesKey).toEqual([]); - expect(renderedLine.lineGeometry.geometryId.specId).toEqual(SPEC_ID); + expect(renderedLine.lineGeometry.seriesIdentifier.seriesKeys).toEqual([1]); + expect(renderedLine.lineGeometry.seriesIdentifier.specId).toEqual(SPEC_ID); expect(renderedLine.lineGeometry.transform).toEqual({ x: 0, y: 0 }); }); test('Can render points', () => { @@ -991,14 +1024,12 @@ describe('Rendering points - line', () => { beforeEach(() => { renderedLine = renderLine( 25, // adding a ideal 25px shift, generally applied by renderGeometries - pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0].data, + pointSeriesDomains.formattedDataSeries.nonStacked[0].dataSeries[0], xScale, yScales.get(GROUP_ID)!, 'red', CurveType.LINEAR, - SPEC_ID, false, - [], 0, LIGHT_THEME.lineSeriesStyle, ); @@ -1012,14 +1043,17 @@ describe('Rendering points - line', () => { expect(points.length).toBe(2); // will keep the 3rd point as an indexedGeometry expect(indexedGeometries.size).toEqual(3); - expect(points[0]).toEqual({ + expect(points[0]).toEqual(({ x: 0, y: 100, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -1030,15 +1064,18 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); - expect(points[1]).toEqual({ + } as unknown) as PointGeometry); + expect(points[1]).toEqual(({ x: 50, y: 0, radius: 10, color: 'red', - geometryId: { + seriesIdentifier: { specId: SPEC_ID, - seriesKey: [], + key: 'spec{spec_1}yAccessor{1}splitAccessors{}', + yAccessor: 1, + splitAccessors: new Map(), + seriesKeys: [1], }, value: { accessor: 'y1', @@ -1049,7 +1086,7 @@ describe('Rendering points - line', () => { x: 25, y: 0, }, - } as PointGeometry); + } as unknown) as PointGeometry); }); }); }); diff --git a/src/chart_types/xy_chart/rendering/rendering.test.ts b/src/chart_types/xy_chart/rendering/rendering.test.ts index 0c5f9c7a0e..1280e22ad3 100644 --- a/src/chart_types/xy_chart/rendering/rendering.test.ts +++ b/src/chart_types/xy_chart/rendering/rendering.test.ts @@ -6,11 +6,12 @@ import { getClippedRanges, } from './rendering'; import { BarSeriesStyle, SharedGeometryStateStyle, PointStyle } from '../../../utils/themes/theme'; -import { DataSeriesDatum } from '../utils/series'; +import { DataSeriesDatum, SeriesIdentifier } from '../utils/series'; import { mergePartial, RecursivePartial } from '../../../utils/commons'; -import { BarGeometry, PointGeometry, GeometryId } from '../../../utils/geometry'; +import { BarGeometry, PointGeometry } from '../../../utils/geometry'; import { MockDataSeries } from '../../../mocks'; import { MockScale } from '../../../mocks/scale'; +import { LegendItem } from '../legend/legend'; describe('Rendering utils', () => { test('check if point is in geometry', () => { @@ -34,9 +35,12 @@ describe('Rendering utils', () => { const geometry: BarGeometry = { color: 'red', - geometryId: { - seriesKey: [], + seriesIdentifier: { specId: 'id', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', }, value: { accessor: 'y1', @@ -60,9 +64,12 @@ describe('Rendering utils', () => { test('check if point is in point geometry', () => { const geometry: PointGeometry = { color: 'red', - geometryId: { - seriesKey: [], + seriesIdentifier: { specId: 'id', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', }, value: { accessor: 'y1', @@ -87,19 +94,19 @@ describe('Rendering utils', () => { expect(isPointOnGeometry(11, 11, geometry)).toBe(false); }); - test('should get common geometry style dependent on legend item highlight state', () => { - const geometryId = { - seriesKey: [], + describe('should get common geometry style dependent on legend item highlight state', () => { + const seriesIdentifier: SeriesIdentifier = { specId: 'id', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'somekey', }; - const highlightedLegendItem = { - key: '', + const highlightedLegendItem: LegendItem = { + key: 'somekey', color: '', label: '', - value: { - colorValues: [], - specId: 'id', - }, + seriesIdentifier, isSeriesVisible: true, isLegendItemVisible: true, displayValue: { @@ -114,11 +121,11 @@ describe('Rendering utils', () => { }, }; - const unhighlightedLegendItem = { + const unhighlightedLegendItem: LegendItem = { ...highlightedLegendItem, - value: { - colorValues: [], - specId: 'foo', + seriesIdentifier: { + ...seriesIdentifier, + key: 'not me', }, }; @@ -134,46 +141,58 @@ describe('Rendering utils', () => { }, }; - // no highlighted elements - const defaultStyle = getGeometryStateStyle(geometryId, null, sharedThemeStyle); - expect(defaultStyle).toBe(sharedThemeStyle.default); + it('no highlighted elements', () => { + const defaultStyle = getGeometryStateStyle(seriesIdentifier, null, sharedThemeStyle); + expect(defaultStyle).toBe(sharedThemeStyle.default); + }); - // should equal highlighted opacity - const highlightedStyle = getGeometryStateStyle(geometryId, highlightedLegendItem, sharedThemeStyle); - expect(highlightedStyle).toBe(sharedThemeStyle.highlighted); + it('should equal highlighted opacity', () => { + const highlightedStyle = getGeometryStateStyle(seriesIdentifier, highlightedLegendItem, sharedThemeStyle); + expect(highlightedStyle).toBe(sharedThemeStyle.highlighted); + }); - // should equal unhighlighted opacity - const unhighlightedStyle = getGeometryStateStyle(geometryId, unhighlightedLegendItem, sharedThemeStyle); - expect(unhighlightedStyle).toBe(sharedThemeStyle.unhighlighted); + it('should equal unhighlighted when not highlighted item', () => { + const unhighlightedStyle = getGeometryStateStyle(seriesIdentifier, unhighlightedLegendItem, sharedThemeStyle); + expect(unhighlightedStyle).toBe(sharedThemeStyle.unhighlighted); + }); - // should equal custom spec highlighted opacity - const customHighlightedStyle = getGeometryStateStyle(geometryId, highlightedLegendItem, sharedThemeStyle); - expect(customHighlightedStyle).toBe(sharedThemeStyle.highlighted); + it('should equal custom spec highlighted opacity', () => { + const customHighlightedStyle = getGeometryStateStyle(seriesIdentifier, highlightedLegendItem, sharedThemeStyle); + expect(customHighlightedStyle).toBe(sharedThemeStyle.highlighted); + }); - // unhighlighted elements remain unchanged with custom opacity - const customUnhighlightedStyle = getGeometryStateStyle(geometryId, unhighlightedLegendItem, sharedThemeStyle); - expect(customUnhighlightedStyle).toBe(sharedThemeStyle.unhighlighted); + it('unhighlighted elements remain unchanged with custom opacity', () => { + const customUnhighlightedStyle = getGeometryStateStyle( + seriesIdentifier, + unhighlightedLegendItem, + sharedThemeStyle, + ); + expect(customUnhighlightedStyle).toBe(sharedThemeStyle.unhighlighted); + }); - // has individual highlight - const hasIndividualHighlight = getGeometryStateStyle(geometryId, null, sharedThemeStyle, { - hasHighlight: true, - hasGeometryHover: true, + it('has individual highlight', () => { + const hasIndividualHighlight = getGeometryStateStyle(seriesIdentifier, null, sharedThemeStyle, { + hasHighlight: true, + hasGeometryHover: true, + }); + expect(hasIndividualHighlight).toBe(sharedThemeStyle.highlighted); }); - expect(hasIndividualHighlight).toBe(sharedThemeStyle.highlighted); - // no highlight - const noHighlight = getGeometryStateStyle(geometryId, null, sharedThemeStyle, { - hasHighlight: false, - hasGeometryHover: true, + it('no highlight', () => { + const noHighlight = getGeometryStateStyle(seriesIdentifier, null, sharedThemeStyle, { + hasHighlight: false, + hasGeometryHover: true, + }); + expect(noHighlight).toBe(sharedThemeStyle.unhighlighted); }); - expect(noHighlight).toBe(sharedThemeStyle.unhighlighted); - // no geometry hover - const noHover = getGeometryStateStyle(geometryId, null, sharedThemeStyle, { - hasHighlight: true, - hasGeometryHover: false, + it('no geometry hover', () => { + const noHover = getGeometryStateStyle(seriesIdentifier, null, sharedThemeStyle, { + hasHighlight: true, + hasGeometryHover: false, + }); + expect(noHover).toBe(sharedThemeStyle.highlighted); }); - expect(noHover).toBe(sharedThemeStyle.highlighted); }); describe('getBarStyleOverrides', () => { @@ -203,9 +222,12 @@ describe('Rendering utils', () => { initialY1: 4, initialY0: 5, }; - const geometryId: GeometryId = { + const seriesIdentifier: SeriesIdentifier = { specId: 'test', - seriesKey: ['test'], + yAccessor: 'test', + splitAccessors: new Map(), + seriesKeys: ['test'], + key: '', }; beforeEach(() => { @@ -213,28 +235,28 @@ describe('Rendering utils', () => { }); it('should return input seriesStyle if no barStyleAccessor is passed', () => { - const styleOverrides = getBarStyleOverrides(datum, geometryId, sampleSeriesStyle); + const styleOverrides = getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle); expect(styleOverrides).toBe(sampleSeriesStyle); }); it('should return input seriesStyle if barStyleAccessor returns null', () => { mockAccessor.mockReturnValue(null); - const styleOverrides = getBarStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); + const styleOverrides = getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle, mockAccessor); expect(styleOverrides).toBe(sampleSeriesStyle); }); - it('should call barStyleAccessor with datum and geometryId', () => { - getBarStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); + it('should call barStyleAccessor with datum and seriesIdentifier', () => { + getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle, mockAccessor); - expect(mockAccessor).toBeCalledWith(datum, geometryId); + expect(mockAccessor).toBeCalledWith(datum, seriesIdentifier); }); it('should return seriesStyle with updated fill color', () => { const color = 'blue'; mockAccessor.mockReturnValue(color); - const styleOverrides = getBarStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); + const styleOverrides = getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle, mockAccessor); const expectedStyles: BarSeriesStyle = { ...sampleSeriesStyle, rect: { @@ -247,7 +269,7 @@ describe('Rendering utils', () => { it('should return a new seriesStyle object with color', () => { mockAccessor.mockReturnValue('blue'); - const styleOverrides = getBarStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); + const styleOverrides = getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle, mockAccessor); expect(styleOverrides).not.toBe(sampleSeriesStyle); }); @@ -262,7 +284,7 @@ describe('Rendering utils', () => { }, }; mockAccessor.mockReturnValue(partialStyle); - const styleOverrides = getBarStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); + const styleOverrides = getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle, mockAccessor); const expectedStyles = mergePartial(sampleSeriesStyle, partialStyle, { mergeOptionalPartialValues: true, }); @@ -276,7 +298,7 @@ describe('Rendering utils', () => { fill: 'blue', }, }); - const styleOverrides = getBarStyleOverrides(datum, geometryId, sampleSeriesStyle, mockAccessor); + const styleOverrides = getBarStyleOverrides(datum, seriesIdentifier, sampleSeriesStyle, mockAccessor); expect(styleOverrides).not.toBe(sampleSeriesStyle); }); @@ -292,9 +314,12 @@ describe('Rendering utils', () => { initialY1: 4, initialY0: 5, }; - const geometryId: GeometryId = { + const seriesIdentifier: SeriesIdentifier = { specId: 'test', - seriesKey: ['test'], + yAccessor: 'test', + splitAccessors: new Map(), + seriesKeys: ['test'], + key: '', }; beforeEach(() => { @@ -302,28 +327,28 @@ describe('Rendering utils', () => { }); it('should return undefined if no pointStyleAccessor is passed', () => { - const styleOverrides = getPointStyleOverrides(datum, geometryId); + const styleOverrides = getPointStyleOverrides(datum, seriesIdentifier); expect(styleOverrides).toBeUndefined(); }); it('should return undefined if pointStyleAccessor returns null', () => { mockAccessor.mockReturnValue(null); - const styleOverrides = getPointStyleOverrides(datum, geometryId, mockAccessor); + const styleOverrides = getPointStyleOverrides(datum, seriesIdentifier, mockAccessor); expect(styleOverrides).toBeUndefined(); }); - it('should call pointStyleAccessor with datum and geometryId', () => { - getPointStyleOverrides(datum, geometryId, mockAccessor); + it('should call pointStyleAccessor with datum and seriesIdentifier', () => { + getPointStyleOverrides(datum, seriesIdentifier, mockAccessor); - expect(mockAccessor).toBeCalledWith(datum, geometryId); + expect(mockAccessor).toBeCalledWith(datum, seriesIdentifier); }); it('should return seriesStyle with updated stroke color', () => { const stroke = 'blue'; mockAccessor.mockReturnValue(stroke); - const styleOverrides = getPointStyleOverrides(datum, geometryId, mockAccessor); + const styleOverrides = getPointStyleOverrides(datum, seriesIdentifier, mockAccessor); const expectedStyles: Partial = { stroke, }; diff --git a/src/chart_types/xy_chart/rendering/rendering.ts b/src/chart_types/xy_chart/rendering/rendering.ts index 27e81945d7..6a387c64b8 100644 --- a/src/chart_types/xy_chart/rendering/rendering.ts +++ b/src/chart_types/xy_chart/rendering/rendering.ts @@ -8,12 +8,10 @@ import { BarSeriesStyle, GeometryStateStyle, } from '../../../utils/themes/theme'; -import { SpecId } from '../../../utils/ids'; import { isLogarithmicScale } from '../../../utils/scales/scale_continuous'; import { Scale, ScaleType } from '../../../utils/scales/scales'; import { CurveType, getCurveFactory } from '../../../utils/curves'; -import { DataSeriesDatum } from '../utils/series'; -import { belongsToDataSeries } from '../utils/series_utils'; +import { DataSeriesDatum, SeriesIdentifier, DataSeries } from '../utils/series'; import { DisplayValueSpec, PointStyleAccessor, BarStyleAccessor } from '../utils/specs'; import { IndexedGeometry, @@ -21,10 +19,9 @@ import { BarGeometry, AreaGeometry, LineGeometry, - GeometryId, isPointGeometry, - AccessorType, ClippedRanges, + BandedAccessorType, } from '../../../utils/geometry'; import { mergePartial } from '../../../utils/commons'; import { LegendItem } from '../legend/legend'; @@ -45,10 +42,10 @@ export function mutableIndexedGeometryMapUpsert( export function getPointStyleOverrides( datum: DataSeriesDatum, - geometryId: GeometryId, + seriesIdentifier: SeriesIdentifier, pointStyleAccessor?: PointStyleAccessor, ): Partial | undefined { - const styleOverride = pointStyleAccessor && pointStyleAccessor(datum, geometryId); + const styleOverride = pointStyleAccessor && pointStyleAccessor(datum, seriesIdentifier); if (!styleOverride) { return; @@ -65,11 +62,11 @@ export function getPointStyleOverrides( export function getBarStyleOverrides( datum: DataSeriesDatum, - geometryId: GeometryId, + seriesIdentifier: SeriesIdentifier, seriesStyle: BarSeriesStyle, styleAccessor?: BarStyleAccessor, ): BarSeriesStyle { - const styleOverride = styleAccessor && styleAccessor(datum, geometryId); + const styleOverride = styleAccessor && styleAccessor(datum, seriesIdentifier); if (!styleOverride) { return seriesStyle; @@ -92,13 +89,11 @@ export function getBarStyleOverrides( function renderPoints( shift: number, - dataset: DataSeriesDatum[], + dataSeries: DataSeries, xScale: Scale, yScale: Scale, color: string, - specId: SpecId, hasY0Accessors: boolean, - seriesKey: any[], styleAccessor?: PointStyleAccessor, ): { pointGeometries: PointGeometry[]; @@ -106,7 +101,7 @@ function renderPoints( } { const indexedGeometries: Map = new Map(); const isLogScale = isLogarithmicScale(yScale); - const pointGeometries = dataset.reduce( + const pointGeometries = dataSeries.data.reduce( (acc, datum) => { const { x: xValue, y0, y1, initialY0, initialY1, filled } = datum; // don't create the point if not within the xScale domain or it that point was filled @@ -132,11 +127,14 @@ function renderPoints( y = yScale.scale(yDatum); } const originalY = hasY0Accessors && index === 0 ? initialY0 : initialY1; - const geometryId = { - specId, - seriesKey, + const seriesIdentifier: SeriesIdentifier = { + key: dataSeries.key, + specId: dataSeries.specId, + yAccessor: dataSeries.yAccessor, + splitAccessors: dataSeries.splitAccessors, + seriesKeys: dataSeries.seriesKeys, }; - const styleOverrides = getPointStyleOverrides(datum, geometryId, styleAccessor); + const styleOverrides = getPointStyleOverrides(datum, seriesIdentifier, styleAccessor); const pointGeometry: PointGeometry = { radius, x, @@ -145,13 +143,13 @@ function renderPoints( value: { x: xValue, y: originalY, - accessor: hasY0Accessors && index === 0 ? AccessorType.Y0 : AccessorType.Y1, + accessor: hasY0Accessors && index === 0 ? BandedAccessorType.Y0 : BandedAccessorType.Y1, }, transform: { x: shift, y: 0, }, - geometryId, + seriesIdentifier, styleOverrides, }; mutableIndexedGeometryMapUpsert(indexedGeometries, xValue, pointGeometry); @@ -173,12 +171,10 @@ function renderPoints( export function renderBars( orderIndex: number, - dataset: DataSeriesDatum[], + dataSeries: DataSeries, xScale: Scale, yScale: Scale, color: string, - specId: SpecId, - seriesKey: any[], sharedSeriesStyle: BarSeriesStyle, displayValueSettings?: DisplayValueSpec, styleAccessor?: BarStyleAccessor, @@ -198,7 +194,7 @@ export function renderBars( const fontFamily = sharedSeriesStyle.displayValue.fontFamily; const absMinHeight = minBarHeight && Math.abs(minBarHeight); - dataset.forEach((datum) => { + dataSeries.data.forEach((datum) => { const { y0, y1, initialY1, filled } = datum; // don't create a bar if the initialY1 value is null. if (y1 === null || initialY1 === null || (filled && filled.y1 !== undefined)) { @@ -276,12 +272,15 @@ export function renderBars( } : undefined; - const geometryId = { - specId, - seriesKey, + const seriesIdentifier: SeriesIdentifier = { + key: dataSeries.key, + specId: dataSeries.specId, + yAccessor: dataSeries.yAccessor, + splitAccessors: dataSeries.splitAccessors, + seriesKeys: dataSeries.seriesKeys, }; - const seriesStyle = getBarStyleOverrides(datum, geometryId, sharedSeriesStyle, styleAccessor); + const seriesStyle = getBarStyleOverrides(datum, seriesIdentifier, sharedSeriesStyle, styleAccessor); const barGeometry: BarGeometry = { displayValue, @@ -293,9 +292,9 @@ export function renderBars( value: { x: datum.x, y: initialY1, - accessor: AccessorType.Y1, + accessor: BandedAccessorType.Y1, }, - geometryId, + seriesIdentifier, seriesStyle, }; mutableIndexedGeometryMapUpsert(indexedGeometries, datum.x, barGeometry); @@ -312,14 +311,12 @@ export function renderBars( export function renderLine( shift: number, - dataset: DataSeriesDatum[], + dataSeries: DataSeries, xScale: Scale, yScale: Scale, color: string, curve: CurveType, - specId: SpecId, hasY0Accessors: boolean, - seriesKey: any[], xScaleOffset: number, seriesStyle: LineSeriesStyle, pointStyleAccessor?: PointStyleAccessor, @@ -352,28 +349,29 @@ export function renderLine( const { pointGeometries, indexedGeometries } = renderPoints( shift - xScaleOffset, - dataset, + dataSeries, xScale, yScale, color, - specId, hasY0Accessors, - seriesKey, pointStyleAccessor, ); - const clippedRanges = hasFit && !hasY0Accessors ? getClippedRanges(dataset, xScale, xScaleOffset) : []; + const clippedRanges = hasFit && !hasY0Accessors ? getClippedRanges(dataSeries.data, xScale, xScaleOffset) : []; const lineGeometry = { - line: pathGenerator(dataset) || '', + line: pathGenerator(dataSeries.data) || '', points: pointGeometries, color, transform: { x, y, }, - geometryId: { - specId, - seriesKey, + seriesIdentifier: { + key: dataSeries.key, + specId: dataSeries.specId, + yAccessor: dataSeries.yAccessor, + splitAccessors: dataSeries.splitAccessors, + seriesKeys: dataSeries.seriesKeys, }, seriesLineStyle: seriesStyle.line, seriesPointStyle: seriesStyle.point, @@ -402,14 +400,12 @@ export const getYValue = ({ y1, filled }: DataSeriesDatum): number | null => { export function renderArea( shift: number, - dataset: DataSeriesDatum[], + dataSeries: DataSeries, xScale: Scale, yScale: Scale, color: string, curve: CurveType, - specId: SpecId, hasY0Accessors: boolean, - seriesKey: any[], xScaleOffset: number, seriesStyle: AreaSeriesStyle, isStacked = false, @@ -442,14 +438,15 @@ export function renderArea( }) .curve(getCurveFactory(curve)); - const clippedRanges = hasFit && !hasY0Accessors && !isStacked ? getClippedRanges(dataset, xScale, xScaleOffset) : []; - const y1Line = pathGenerator.lineY1()(dataset); + const clippedRanges = + hasFit && !hasY0Accessors && !isStacked ? getClippedRanges(dataSeries.data, xScale, xScaleOffset) : []; + const y1Line = pathGenerator.lineY1()(dataSeries.data); const lines: string[] = []; if (y1Line) { lines.push(y1Line); } if (hasY0Accessors) { - const y0Line = pathGenerator.lineY0()(dataset); + const y0Line = pathGenerator.lineY0()(dataSeries.data); if (y0Line) { lines.push(y0Line); } @@ -457,18 +454,16 @@ export function renderArea( const { pointGeometries, indexedGeometries } = renderPoints( shift - xScaleOffset, - dataset, + dataSeries, xScale, yScale, color, - specId, hasY0Accessors, - seriesKey, pointStyleAccessor, ); const areaGeometry: AreaGeometry = { - area: pathGenerator(dataset) || '', + area: pathGenerator(dataSeries.data) || '', lines, points: pointGeometries, color, @@ -476,9 +471,12 @@ export function renderArea( y: 0, x: shift, }, - geometryId: { - specId, - seriesKey, + seriesIdentifier: { + key: dataSeries.key, + specId: dataSeries.specId, + yAccessor: dataSeries.yAccessor, + splitAccessors: dataSeries.splitAccessors, + seriesKeys: dataSeries.seriesKeys, }, seriesAreaStyle: seriesStyle.area, seriesAreaLineStyle: seriesStyle.line, @@ -528,7 +526,7 @@ export function getClippedRanges(dataset: DataSeriesDatum[], xScale: Scale, xSca } export function getGeometryStateStyle( - geometryId: GeometryId, + seriesIdentifier: SeriesIdentifier, highlightedLegendItem: LegendItem | null, sharedGeometryStyle: SharedGeometryStateStyle, individualHighlight?: { [key: string]: boolean }, @@ -536,7 +534,7 @@ export function getGeometryStateStyle( const { default: defaultStyles, highlighted, unhighlighted } = sharedGeometryStyle; if (highlightedLegendItem != null) { - const isPartOfHighlightedSeries = belongsToDataSeries(geometryId, highlightedLegendItem.value); + const isPartOfHighlightedSeries = seriesIdentifier.key === highlightedLegendItem.seriesIdentifier.key; return isPartOfHighlightedSeries ? highlighted : unhighlighted; } @@ -571,6 +569,6 @@ export function isPointOnGeometry( return yCoordinate >= y && yCoordinate <= y + height && xCoordinate >= x && xCoordinate <= x + width; } -export function getGeometryIdKey(geometryId: GeometryId, prefix?: string, postfix?: string) { - return `${prefix || ''}spec:${geometryId.specId}_${geometryId.seriesKey.join('::-::')}${postfix || ''}`; +export function getSeriesIdentifierPrefixedKey(seriesIdentifier: SeriesIdentifier, prefix?: string, postfix?: string) { + return `${prefix || ''}${seriesIdentifier.key}${postfix || ''}`; } diff --git a/src/chart_types/xy_chart/state/__snapshots__/utils.test.ts.snap b/src/chart_types/xy_chart/state/__snapshots__/utils.test.ts.snap index 299011ed3c..e05c85405e 100644 --- a/src/chart_types/xy_chart/state/__snapshots__/utils.test.ts.snap +++ b/src/chart_types/xy_chart/state/__snapshots__/utils.test.ts.snap @@ -56,9 +56,13 @@ Array [ "y1": 6, }, ], - "key": Array [], - "seriesColorKey": "specId:{spec1},colors:{}", + "key": "spec{spec1}yAccessor{y}splitAccessors{}", + "seriesKeys": Array [ + "y", + ], "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y", }, ], "groupId": "group1", @@ -117,9 +121,13 @@ Array [ "y1": 6, }, ], - "key": Array [], - "seriesColorKey": "specId:{spec2},colors:{}", + "key": "spec{spec2}yAccessor{y}splitAccessors{}", + "seriesKeys": Array [ + "y", + ], "specId": "spec2", + "splitAccessors": Map {}, + "yAccessor": "y", }, ], "groupId": "group2", @@ -187,11 +195,16 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "spec{spec2}yAccessor{y}splitAccessors{g-a}", + "seriesKeys": Array [ "a", + "y", ], - "seriesColorKey": "specId:{spec2},colors:{a}", "specId": "spec2", + "splitAccessors": Map { + "g" => "a", + }, + "yAccessor": "y", }, Object { "data": Array [ @@ -244,11 +257,16 @@ Array [ "y1": 9, }, ], - "key": Array [ + "key": "spec{spec2}yAccessor{y}splitAccessors{g-b}", + "seriesKeys": Array [ "b", + "y", ], - "seriesColorKey": "specId:{spec2},colors:{b}", "specId": "spec2", + "splitAccessors": Map { + "g" => "b", + }, + "yAccessor": "y", }, ], "groupId": "group2", @@ -316,11 +334,16 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y}splitAccessors{g-a}", + "seriesKeys": Array [ "a", + "y", ], - "seriesColorKey": "specId:{spec1},colors:{a}", "specId": "spec1", + "splitAccessors": Map { + "g" => "a", + }, + "yAccessor": "y", }, Object { "data": Array [ @@ -373,11 +396,16 @@ Array [ "y1": 5, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y}splitAccessors{g-b}", + "seriesKeys": Array [ "b", + "y", ], - "seriesColorKey": "specId:{spec1},colors:{b}", "specId": "spec1", + "splitAccessors": Map { + "g" => "b", + }, + "yAccessor": "y", }, ], "groupId": "group1", diff --git a/src/chart_types/xy_chart/state/chart_state.test.ts b/src/chart_types/xy_chart/state/chart_state.test.ts index b9d103517e..76e8584e6a 100644 --- a/src/chart_types/xy_chart/state/chart_state.test.ts +++ b/src/chart_types/xy_chart/state/chart_state.test.ts @@ -16,7 +16,7 @@ import { ScaleBand } from '../../../utils/scales/scale_band'; import { ScaleContinuous } from '../../../utils/scales/scale_continuous'; import { ScaleType } from '../../../utils/scales/scales'; // import { ChartStore } from './chart_state'; -import { IndexedGeometry, GeometryValue, AccessorType } from '../../../utils/geometry'; +import { IndexedGeometry, GeometryValue, BandedAccessorType } from '../../../utils/geometry'; import { AxisTicksDimensions, isDuplicateAxis } from '../utils/axis_utils'; import { AxisId } from '../../../utils/ids'; import { LegendItem } from '../legend/legend'; @@ -48,9 +48,12 @@ describe.skip('Chart Store', () => { key: 'color1', color: 'foo', label: 'bar', - value: { + seriesIdentifier: { specId: SPEC_ID, - colorValues: [], + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color1', }, displayValue: { raw: { @@ -68,9 +71,12 @@ describe.skip('Chart Store', () => { key: 'color2', color: 'baz', label: 'qux', - value: { + seriesIdentifier: { specId: SPEC_ID, - colorValues: [], + yAccessor: '', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color2', }, displayValue: { raw: { @@ -244,7 +250,7 @@ describe.skip('Chart Store', () => { test.skip('can initialize deselectedDataSeries depending on previous state', () => { store.specsInitialized.set(false); store.computeChart(); - expect(store.deselectedDataSeries).toEqual(null); + expect(store.deselectedDataSeries).toEqual([]); }); test.skip('can add an axis', () => { @@ -304,7 +310,7 @@ describe.skip('Chart Store', () => { store.setOnLegendItemOverListener(legendListener); store.onLegendItemOver(secondLegendItem.key); - expect(legendListener).toBeCalledWith(secondLegendItem.value); + expect(legendListener).toBeCalledWith(secondLegendItem.seriesIdentifier); store.onLegendItemOver(null); expect(legendListener).toBeCalledWith(null); @@ -343,7 +349,7 @@ describe.skip('Chart Store', () => { store.highlightedLegendItemKey.set(null); store.toggleSeriesVisibility(firstLegendItem.key); - expect(store.deselectedDataSeries).toEqual([firstLegendItem.value]); + expect(store.deselectedDataSeries).toEqual([firstLegendItem.seriesIdentifier]); expect(store.highlightedLegendItemKey.get()).toBe(null); store.onLegendItemOver(firstLegendItem.key); expect(store.highlightedLegendItemKey.get()).toBe(null); @@ -383,7 +389,7 @@ describe.skip('Chart Store', () => { // store.setOnLegendItemClickListener(legendListener); // store.onLegendItemClick(secondLegendItem.key); // expect(store.selectedLegendItemKey.get()).toBe(secondLegendItem.key); - expect(legendListener).toBeCalledWith(secondLegendItem.value); + expect(legendListener).toBeCalledWith(firstLegendItem.seriesIdentifier); }); test.skip('can respond to a legend item plus click event', () => { @@ -406,7 +412,7 @@ describe.skip('Chart Store', () => { store.selectedLegendItemKey.set(firstLegendItem.key); store.onLegendItemPlusClick(); - expect(legendListener).toBeCalledWith(firstLegendItem.value); + expect(legendListener).toBeCalledWith(firstLegendItem.seriesIdentifier); }); test.skip('can respond to a legend item minus click event', () => { @@ -429,7 +435,7 @@ describe.skip('Chart Store', () => { store.selectedLegendItemKey.set(firstLegendItem.key); store.onLegendItemMinusClick(); - expect(legendListener).toBeCalledWith(firstLegendItem.value); + expect(legendListener).toBeCalledWith(firstLegendItem.seriesIdentifier); }); test.skip('can toggle series visibility', () => { @@ -440,19 +446,19 @@ describe.skip('Chart Store', () => { ); store.legendItems = new Map([[firstLegendItem.key, firstLegendItem], [secondLegendItem.key, secondLegendItem]]); - store.deselectedDataSeries = null; + store.deselectedDataSeries = []; store.computeChart = computeChart; store.toggleSeriesVisibility('other'); - expect(store.deselectedDataSeries).toEqual(null); + expect(store.deselectedDataSeries).toEqual([]); expect(computeChart).not.toBeCalled(); - store.deselectedDataSeries = [firstLegendItem.value, secondLegendItem.value]; + store.deselectedDataSeries = [firstLegendItem.seriesIdentifier, secondLegendItem.seriesIdentifier]; store.toggleSeriesVisibility(firstLegendItem.key); - expect(store.deselectedDataSeries).toEqual([secondLegendItem.value]); + expect(store.deselectedDataSeries).toEqual([secondLegendItem.seriesIdentifier]); expect(computeChart).toBeCalled(); - store.deselectedDataSeries = [firstLegendItem.value]; + store.deselectedDataSeries = [firstLegendItem.seriesIdentifier]; store.toggleSeriesVisibility(firstLegendItem.key); expect(store.deselectedDataSeries).toEqual([]); }); @@ -465,18 +471,18 @@ describe.skip('Chart Store', () => { ); store.legendItems = new Map([[firstLegendItem.key, firstLegendItem], [secondLegendItem.key, secondLegendItem]]); - store.deselectedDataSeries = null; + store.deselectedDataSeries = []; store.computeChart = computeChart; store.toggleSingleSeries('other'); - expect(store.deselectedDataSeries).toEqual(null); + expect(store.deselectedDataSeries).toEqual([]); expect(computeChart).not.toBeCalled(); store.toggleSingleSeries(firstLegendItem.key); - expect(store.deselectedDataSeries).toEqual([firstLegendItem.value]); + expect(store.deselectedDataSeries).toEqual([firstLegendItem.seriesIdentifier]); store.toggleSingleSeries(firstLegendItem.key); - expect(store.deselectedDataSeries).toEqual([secondLegendItem.value]); + expect(store.deselectedDataSeries).toEqual([secondLegendItem.seriesIdentifier]); }); test.skip('can set an element click listener', () => { @@ -748,37 +754,34 @@ describe.skip('Chart Store', () => { }); test.skip('can set the color for a series', () => { - const computeChart = jest.fn( - (): void => { - return; - }, - ); - store.computeChart = computeChart; - store.legendItems = new Map([[firstLegendItem.key, firstLegendItem], [secondLegendItem.key, secondLegendItem]]); + beforeEach(() => { + store.computeChart = jest.fn(); + store.legendItems = new Map([[firstLegendItem.key, firstLegendItem], [secondLegendItem.key, secondLegendItem]]); + }); - store.setSeriesColor('other', 'foo'); - expect(computeChart).not.toBeCalled(); - expect(store.customSeriesColors).toEqual(new Map()); + it('should set color override', () => { + store.setSeriesColor(firstLegendItem.key, 'red'); + expect(store.computeChart).toBeCalled(); + expect(store.seriesColorOverrides.get(firstLegendItem.key)).toBe('red'); + }); - store.setSeriesColor(firstLegendItem.key, 'foo'); - expect(computeChart).toBeCalled(); - expect(store.seriesSpecs.get(firstLegendItem.value.specId)).toBeUndefined(); + it('should not set color override with empty color', () => { + store.setSeriesColor(firstLegendItem.key, ''); + expect(store.computeChart).not.toBeCalled(); + expect(store.seriesColorOverrides.get(firstLegendItem.key)).toBeUndefined(); + }); - store.addSeriesSpec(spec); - store.setSeriesColor(firstLegendItem.key, 'foo'); - const expectedSpecCustomColorSeries = new Map(); - expectedSpecCustomColorSeries.set(firstLegendItem.value, 'foo'); - expect(spec.customSeriesColors).toEqual(expectedSpecCustomColorSeries); - - store.setSeriesColor(secondLegendItem.key, 'bar'); - expectedSpecCustomColorSeries.set(secondLegendItem.value, 'bar'); - expect(spec.customSeriesColors).toEqual(expectedSpecCustomColorSeries); + it('should not set color override with empty key', () => { + store.setSeriesColor('', 'red'); + expect(store.computeChart).not.toBeCalled(); + expect(store.seriesColorOverrides.get(firstLegendItem.key)).toBeUndefined(); + }); }); test.skip('can reset selectedDataSeries', () => { - store.deselectedDataSeries = [firstLegendItem.value]; + store.deselectedDataSeries = [firstLegendItem.seriesIdentifier]; store.resetDeselectedDataSeries(); - expect(store.deselectedDataSeries).toBe(null); + expect(store.deselectedDataSeries).toStrictEqual([]); }); test.skip('can update the crosshair visibility', () => { store.cursorPosition.x = -1; @@ -811,6 +814,7 @@ describe.skip('Chart Store', () => { isXValue: false, seriesKey: 'a', yAccessor: 'y', + isVisible: true, }; store.cursorPosition.x = -1; store.cursorPosition.y = 1; @@ -923,6 +927,7 @@ describe.skip('Chart Store', () => { isXValue: false, seriesKey: 'a', yAccessor: 'y', + isVisible: true, }; store.xScale = new ScaleContinuous({ type: ScaleType.Linear, domain: [0, 100], range: [0, 100] }); store.cursorPosition.x = 1; @@ -961,9 +966,12 @@ describe.skip('Chart Store', () => { }; const geom1: IndexedGeometry = { color: 'red', - geometryId: { + seriesIdentifier: { specId: 'specId1', - seriesKey: [2], + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [2], + key: '', }, value: { x: 0, @@ -978,9 +986,12 @@ describe.skip('Chart Store', () => { }; const geom2: IndexedGeometry = { color: 'blue', - geometryId: { + seriesIdentifier: { specId: 'specId2', - seriesKey: [2], + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [2], + key: '', }, value: { x: 0, @@ -1054,7 +1065,7 @@ describe.skip('Chart Store', () => { store.annotationSpecs.set(rectAnnotationSpec.id, rectAnnotationSpec); store.annotationDimensions.set(rectAnnotationSpec.id, annotationDimensions); - const highlightedTooltipValue = { + const highlightedTooltipValue: TooltipValue = { name: 'foo', value: 1, color: 'color', @@ -1062,8 +1073,9 @@ describe.skip('Chart Store', () => { isXValue: false, seriesKey: 'foo', yAccessor: 'y', + isVisible: true, }; - const unhighlightedTooltipValue = { + const unhighlightedTooltipValue: TooltipValue = { name: 'foo', value: 1, color: 'color', @@ -1071,6 +1083,7 @@ describe.skip('Chart Store', () => { isXValue: false, seriesKey: 'foo', yAccessor: 'y', + isVisible: true, }; const expectedRectTooltipState = { @@ -1099,7 +1112,8 @@ describe.skip('Chart Store', () => { isHighlighted: false, isXValue: true, seriesKey: 'headerSeries', - yAccessor: AccessorType.Y0, + isVisible: true, + yAccessor: BandedAccessorType.Y0, }; store.tooltipData.replace([headerValue]); @@ -1111,13 +1125,14 @@ describe.skip('Chart Store', () => { color: 'a', isHighlighted: false, isXValue: false, - seriesKey: 'seriesKey', - yAccessor: AccessorType.Y1, + seriesKey: 'seriesKeys', + isVisible: true, + yAccessor: BandedAccessorType.Y1, }; store.tooltipData.replace([headerValue, tooltipValue]); const expectedTooltipValues = new Map(); - expectedTooltipValues.set('seriesKey', { + expectedTooltipValues.set('seriesKeys', { y0: undefined, y1: 123, }); @@ -1159,9 +1174,12 @@ describe.skip('Chart Store', () => { store.onBrushEndListener = brushEndListener; const geom1: IndexedGeometry = { color: 'red', - geometryId: { + seriesIdentifier: { specId: 'specId1', - seriesKey: [2], + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [2], + key: '', }, value: { x: 0, diff --git a/src/chart_types/xy_chart/state/selectors/compute_legend.ts b/src/chart_types/xy_chart/state/selectors/compute_legend.ts index 1f54097f50..b91f665dff 100644 --- a/src/chart_types/xy_chart/state/selectors/compute_legend.ts +++ b/src/chart_types/xy_chart/state/selectors/compute_legend.ts @@ -2,7 +2,7 @@ import createCachedSelector from 're-reselect'; import { computeSeriesDomainsSelector } from './compute_series_domains'; import { getSeriesSpecsSelector, getAxisSpecsSelector } from './get_specs'; import { getChartThemeSelector } from '../../../../state/selectors/get_chart_theme'; -import { getSeriesColorMapSelector } from './get_series_color_map'; +import { getSeriesColorsSelector } from './get_series_color_map'; import { computeLegend, LegendItem } from '../../legend/legend'; import { GlobalChartState } from '../../../../state/chart_state'; import { getChartIdSelector } from '../../../../state/selectors/get_chart_id'; @@ -14,7 +14,7 @@ export const computeLegendSelector = createCachedSelector( getSeriesSpecsSelector, computeSeriesDomainsSelector, getChartThemeSelector, - getSeriesColorMapSelector, + getSeriesColorsSelector, getAxisSpecsSelector, getDeselectedSeriesSelector, ], @@ -22,13 +22,13 @@ export const computeLegendSelector = createCachedSelector( seriesSpecs, seriesDomainsAndData, chartTheme, - seriesColorMap, + seriesColors, axesSpecs, deselectedDataSeries, ): Map => { return computeLegend( - seriesDomainsAndData.seriesColors, - seriesColorMap, + seriesDomainsAndData.seriesCollection, + seriesColors, seriesSpecs, chartTheme.colors.defaultVizColor, axesSpecs, diff --git a/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts b/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts index ca82793476..bb5a83e03d 100644 --- a/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts +++ b/src/chart_types/xy_chart/state/selectors/compute_series_domains.ts @@ -2,8 +2,7 @@ import createCachedSelector from 're-reselect'; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_specs'; import { getSeriesSpecsSelector } from './get_specs'; import { mergeYCustomDomainsByGroupIdSelector } from './merge_y_custom_domains'; -import { computeSeriesDomains } from '../utils'; -import { SeriesDomainsAndData } from '../utils'; +import { computeSeriesDomains, SeriesDomainsAndData } from '../utils'; import { GlobalChartState } from '../../../../state/chart_state'; import { getChartIdSelector } from '../../../../state/selectors/get_chart_id'; diff --git a/src/chart_types/xy_chart/state/selectors/compute_series_geometries.ts b/src/chart_types/xy_chart/state/selectors/compute_series_geometries.ts index db235a2b46..5b0393d98f 100644 --- a/src/chart_types/xy_chart/state/selectors/compute_series_geometries.ts +++ b/src/chart_types/xy_chart/state/selectors/compute_series_geometries.ts @@ -5,7 +5,7 @@ import { getChartThemeSelector } from '../../../../state/selectors/get_chart_the import { getSeriesSpecsSelector, getAxisSpecsSelector } from './get_specs'; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_specs'; import { computeSeriesGeometries, ComputedGeometries } from '../utils'; -import { getSeriesColorMapSelector } from './get_series_color_map'; +import { getSeriesColorsSelector } from './get_series_color_map'; import { computeChartDimensionsSelector } from './compute_chart_dimensions'; import { getChartIdSelector } from '../../../../state/selectors/get_chart_id'; @@ -14,7 +14,7 @@ export const computeSeriesGeometriesSelector = createCachedSelector( getSettingsSpecSelector, getSeriesSpecsSelector, computeSeriesDomainsSelector, - getSeriesColorMapSelector, + getSeriesColorsSelector, getChartThemeSelector, computeChartDimensionsSelector, getAxisSpecsSelector, @@ -24,7 +24,7 @@ export const computeSeriesGeometriesSelector = createCachedSelector( settingsSpec, seriesSpecs, seriesDomainsAndData, - seriesColorMap, + seriesColors, chartTheme, chartDimensions, axesSpecs, @@ -36,7 +36,7 @@ export const computeSeriesGeometriesSelector = createCachedSelector( xDomain, yDomain, formattedDataSeries, - seriesColorMap, + seriesColors, chartTheme, chartDimensions.chartDimensions, settingsSpec.rotation, diff --git a/src/chart_types/xy_chart/state/selectors/get_series_color_map.ts b/src/chart_types/xy_chart/state/selectors/get_series_color_map.ts index a109514788..a38a8ae028 100644 --- a/src/chart_types/xy_chart/state/selectors/get_series_color_map.ts +++ b/src/chart_types/xy_chart/state/selectors/get_series_color_map.ts @@ -1,18 +1,18 @@ import createCachedSelector from 're-reselect'; import { computeSeriesDomainsSelector } from './compute_series_domains'; import { getSeriesSpecsSelector } from './get_specs'; -import { getUpdatedCustomSeriesColors } from '../utils'; -import { getSeriesColorMap } from '../../utils/series'; +import { getSeriesColors } from '../../utils/series'; import { getChartThemeSelector } from '../../../../state/selectors/get_chart_theme'; import { getChartIdSelector } from '../../../../state/selectors/get_chart_id'; +import { getCustomSeriesColors } from '../utils'; -export const getSeriesColorMapSelector = createCachedSelector( +export const getSeriesColorsSelector = createCachedSelector( [getSeriesSpecsSelector, computeSeriesDomainsSelector, getChartThemeSelector], (seriesSpecs, seriesDomainsAndData, chartTheme): Map => { - const updatedCustomSeriesColors = getUpdatedCustomSeriesColors(seriesSpecs); + const updatedCustomSeriesColors = getCustomSeriesColors(seriesSpecs, seriesDomainsAndData.seriesCollection); - const seriesColorMap = getSeriesColorMap( - seriesDomainsAndData.seriesColors, + const seriesColorMap = getSeriesColors( + seriesDomainsAndData.seriesCollection, chartTheme.colors, updatedCustomSeriesColors, ); diff --git a/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts b/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts index c0739b65c9..b7cbdf398f 100644 --- a/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts +++ b/src/chart_types/xy_chart/state/selectors/get_tooltip_values_highlighted_geoms.ts @@ -18,6 +18,7 @@ import { CursorEvent } from '../../../../specs'; import { isValidExternalPointerEvent } from '../../../../utils/events'; import { getChartRotationSelector } from '../../../../state/selectors/get_chart_rotation'; import { getChartIdSelector } from '../../../../state/selectors/get_chart_id'; +import { hasSingleSeriesSelector } from './has_single_series'; const EMPTY_VALUES = Object.freeze({ tooltipValues: [], @@ -38,6 +39,7 @@ export const getTooltipValuesAndGeometriesSelector = createCachedSelector( getProjectedPointerPositionSelector, getOrientedProjectedPointerPositionSelector, getChartRotationSelector, + hasSingleSeriesSelector, getComputedScalesSelector, getElementAtCursorPositionSelector, getTooltipTypeSelector, @@ -55,6 +57,7 @@ function getTooltipAndHighlightFromXValue( projectedPointerPosition: Point, orientedProjectedPointerPosition: Point, chartRotation: Rotation, + hasSingleSeries: boolean, scales: ComputedScales, xMatchingGeoms: IndexedGeometry[], tooltipType: TooltipType, @@ -84,7 +87,7 @@ function getTooltipAndHighlightFromXValue( .filter(({ value: { y } }) => y !== null) .reduce((acc, indexedGeometry) => { const { - geometryId: { specId }, + seriesIdentifier: { specId }, } = indexedGeometry; const spec = getSpecsById(seriesSpecs, specId); @@ -115,14 +118,21 @@ function getTooltipAndHighlightFromXValue( // format the tooltip values const yAxisFormatSpec = [0, 180].includes(chartRotation) ? yAxis : xAxis; - const formattedTooltip = formatTooltip(indexedGeometry, spec, false, isHighlighted, yAxisFormatSpec); + const formattedTooltip = formatTooltip( + indexedGeometry, + spec, + false, + isHighlighted, + hasSingleSeries, + yAxisFormatSpec, + ); // format only one time the x value if (!xValueInfo) { // if we have a tooltipHeaderFormatter, then don't pass in the xAxis as the user will define a formatter const xAxisFormatSpec = [0, 180].includes(chartRotation) ? xAxis : yAxis; const formatterAxis = tooltipHeaderFormatter ? undefined : xAxisFormatSpec; - xValueInfo = formatTooltip(indexedGeometry, spec, true, false, formatterAxis); + xValueInfo = formatTooltip(indexedGeometry, spec, true, false, hasSingleSeries, formatterAxis); return [xValueInfo, ...acc, formattedTooltip]; } diff --git a/src/chart_types/xy_chart/state/selectors/has_single_series.ts b/src/chart_types/xy_chart/state/selectors/has_single_series.ts new file mode 100644 index 0000000000..249e861906 --- /dev/null +++ b/src/chart_types/xy_chart/state/selectors/has_single_series.ts @@ -0,0 +1,10 @@ +import createCachedSelector from 're-reselect'; +import { computeSeriesDomainsSelector } from './compute_series_domains'; +import { getChartIdSelector } from '../../../../state/selectors/get_chart_id'; + +export const hasSingleSeriesSelector = createCachedSelector( + [computeSeriesDomainsSelector], + (seriesDomainsAndData): boolean => { + return Boolean(seriesDomainsAndData) && seriesDomainsAndData.seriesCollection.size > 1; + }, +)(getChartIdSelector); diff --git a/src/chart_types/xy_chart/state/utils.test.ts b/src/chart_types/xy_chart/state/utils.test.ts index 4d62ede289..b26ac95eb0 100644 --- a/src/chart_types/xy_chart/state/utils.test.ts +++ b/src/chart_types/xy_chart/state/utils.test.ts @@ -1,4 +1,3 @@ -import { DataSeriesColorsValues, findDataSeriesByColorValues, getSeriesColorMap } from '../utils/series'; import { AreaSeriesSpec, AxisSpec, @@ -8,6 +7,7 @@ import { LineSeriesSpec, SpecTypes, SeriesTypes, + SeriesColorAccessorFn, } from '../utils/specs'; import { BARCHART_1Y0G, BARCHART_1Y1G } from '../../../utils/data_samples/test_dataset'; import { LIGHT_THEME } from '../../../utils/themes/light_theme'; @@ -18,7 +18,6 @@ import { computeSeriesDomains, computeSeriesGeometries, computeXScaleOffset, - getUpdatedCustomSeriesColors, isAllSeriesDeselected, isChartAnimatable, isHistogramModeEnabled, @@ -27,12 +26,17 @@ import { isVerticalRotation, mergeGeometriesIndexes, setBarSeriesAccessors, + getCustomSeriesColors, } from './utils'; -import { IndexedGeometry, AccessorType } from '../../../utils/geometry'; +import { IndexedGeometry, BandedAccessorType } from '../../../utils/geometry'; import { mergeYCustomDomainsByGroupId } from './selectors/merge_y_custom_domains'; import { updateDeselectedDataSeries } from './utils'; import { LegendItem } from '../legend/legend'; import { ChartTypes } from '../..'; +import { MockSeriesSpecs, MockSeriesSpec } from '../../../mocks/specs'; +import { MockSeriesCollection } from '../../../mocks/series/seriesIdentifiers'; +import { SeededDataGenerator } from '../../../mocks/utils'; +import { SeriesCollectionValue, getSeriesIndex, getSeriesColors } from '../utils/series'; describe('Chart State utils', () => { it('should compute and format specifications for non stacked chart', () => { @@ -146,84 +150,83 @@ describe('Chart State utils', () => { expect(domains.formattedDataSeries.stacked).toMatchSnapshot(); expect(domains.formattedDataSeries.nonStacked).toMatchSnapshot(); }); - it('should check if a DataSeriesColorValues item exists in a list of DataSeriesColorValues', () => { - const dataSeriesValuesA: DataSeriesColorsValues = { - specId: 'a', - colorValues: ['a', 'b', 'c'], + it('should check if a SeriesCollectionValue item exists in a list of SeriesCollectionValue', () => { + const dataSeriesValuesA: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'c'], + key: 'a', + }, }; - - const dataSeriesValuesB: DataSeriesColorsValues = { - specId: 'b', - colorValues: ['a', 'b', 'c'], + const dataSeriesValuesB: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'c'], + key: 'b', + }, }; - - const dataSeriesValuesC: DataSeriesColorsValues = { - specId: 'a', - colorValues: ['a', 'b', 'd'], + const dataSeriesValuesC: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'c', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'd'], + key: 'c', + }, }; - - const deselectedSeries = [dataSeriesValuesA, dataSeriesValuesB]; - - expect(findDataSeriesByColorValues(deselectedSeries, dataSeriesValuesA)).toBe(0); - expect(findDataSeriesByColorValues(deselectedSeries, dataSeriesValuesC)).toBe(-1); - expect(findDataSeriesByColorValues(null, dataSeriesValuesA)).toBe(-1); + const deselectedSeries = [dataSeriesValuesA.seriesIdentifier, dataSeriesValuesB.seriesIdentifier]; + expect(getSeriesIndex(deselectedSeries, dataSeriesValuesA.seriesIdentifier)).toBe(0); + expect(getSeriesIndex(deselectedSeries, dataSeriesValuesC.seriesIdentifier)).toBe(-1); + expect(getSeriesIndex([], dataSeriesValuesA.seriesIdentifier)).toBe(-1); }); - it('should update a list of DataSeriesColorsValues given a selected DataSeriesColorValues item', () => { - const dataSeriesValuesA: DataSeriesColorsValues = { - specId: 'a', - colorValues: ['a', 'b', 'c'], - }; - - const dataSeriesValuesB: DataSeriesColorsValues = { - specId: 'b', - colorValues: ['a', 'b', 'c'], - }; - - const dataSeriesValuesC: DataSeriesColorsValues = { - specId: 'a', - colorValues: ['a', 'b', 'd'], + it('should update a list of SeriesCollectionValue given a selected SeriesCollectionValue item', () => { + const dataSeriesValuesA: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'c'], + key: 'a', + }, }; - - const selectedSeries = [dataSeriesValuesA, dataSeriesValuesB]; - const addedSelectedSeries = [dataSeriesValuesA, dataSeriesValuesB, dataSeriesValuesC]; - const removedSelectedSeries = [dataSeriesValuesB]; - - expect(updateDeselectedDataSeries(selectedSeries, dataSeriesValuesC)).toEqual(addedSelectedSeries); - expect(updateDeselectedDataSeries(selectedSeries, dataSeriesValuesA)).toEqual(removedSelectedSeries); - expect(updateDeselectedDataSeries(null, dataSeriesValuesA)).toEqual([dataSeriesValuesA]); - }); - it('should get an updated customSeriesColor based on specs', () => { - const spec1: BasicSeriesSpec = { - chartType: ChartTypes.XYAxis, - specType: SpecTypes.Series, - id: 'spec1', - groupId: 'group1', - seriesType: SeriesTypes.Line, - yScaleType: ScaleType.Log, - xScaleType: ScaleType.Linear, - xAccessor: 'x', - yAccessors: ['y'], - yScaleToDataExtent: false, - data: BARCHART_1Y0G, + const dataSeriesValuesB: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'c'], + key: 'b', + }, }; - - const emptyCustomSeriesColors = getUpdatedCustomSeriesColors([spec1]); - expect(emptyCustomSeriesColors).toEqual(new Map()); - - const dataSeriesColorValues = { - specId: spec1.id, - colorValues: ['bar'], + const dataSeriesValuesC: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'd'], + key: 'd', + }, }; - spec1.customSeriesColors = new Map(); - spec1.customSeriesColors.set(dataSeriesColorValues, 'custom_color'); - - const updatedCustomSeriesColors = getUpdatedCustomSeriesColors([spec1]); - const expectedCustomSeriesColors = new Map(); - expectedCustomSeriesColors.set('specId:{spec1},colors:{bar}', 'custom_color'); + const selectedSeries = [dataSeriesValuesA.seriesIdentifier, dataSeriesValuesB.seriesIdentifier]; + const addedSelectedSeries = [ + dataSeriesValuesA.seriesIdentifier, + dataSeriesValuesB.seriesIdentifier, + dataSeriesValuesC.seriesIdentifier, + ]; + const removedSelectedSeries = [dataSeriesValuesB.seriesIdentifier]; - expect(updatedCustomSeriesColors).toEqual(expectedCustomSeriesColors); + expect(updateDeselectedDataSeries(selectedSeries, dataSeriesValuesC.seriesIdentifier)).toEqual(addedSelectedSeries); + expect(updateDeselectedDataSeries(selectedSeries, dataSeriesValuesA.seriesIdentifier)).toEqual( + removedSelectedSeries, + ); + expect(updateDeselectedDataSeries([], dataSeriesValuesA.seriesIdentifier)).toEqual([ + dataSeriesValuesA.seriesIdentifier, + ]); }); - test('is horizontal chart rotation', () => { expect(isHorizontalRotation(0)).toBe(true); expect(isHorizontalRotation(180)).toBe(true); @@ -234,7 +237,6 @@ describe('Chart State utils', () => { expect(isVerticalRotation(0)).toBe(false); expect(isVerticalRotation(180)).toBe(false); }); - test('is vertical chart rotation', () => { expect(isVerticalRotation(-90)).toBe(true); expect(isVerticalRotation(90)).toBe(true); @@ -318,6 +320,103 @@ describe('Chart State utils', () => { geometriesCounts.linePoints = 301; expect(isChartAnimatable(geometriesCounts, true)).toBe(false); }); + + describe('getCustomSeriesColors', () => { + const specId1 = 'bar1'; + const specId2 = 'bar2'; + const dg = new SeededDataGenerator(); + // 4 groups generated + const data = dg.generateGroupedSeries(50, 4); + const targetKey = 'spec{bar1}yAccessor{y}splitAccessors{g-b}'; + const seriesColorOverrides = new Map([[targetKey, 'blue']]); + + describe('empty series collection and specs', () => { + it('it should return an empty map', () => { + const actual = getCustomSeriesColors(MockSeriesSpecs.empty(), MockSeriesCollection.empty(), new Map()); + + expect(actual.size).toBe(0); + }); + }); + + describe('series collection is not empty', () => { + it('it should return an empty map if no customSeriesColors', () => { + const barSpec1 = MockSeriesSpec.bar({ id: specId1, data }); + const barSpec2 = MockSeriesSpec.bar({ id: specId2, data }); + const barSeriesSpecs = MockSeriesSpecs.fromSpecs([barSpec1, barSpec2]); + const barSeriesCollection = MockSeriesCollection.fromSpecs(barSeriesSpecs); + const actual = getCustomSeriesColors(barSeriesSpecs, barSeriesCollection, new Map()); + + expect(actual.size).toBe(0); + }); + + describe('with customSeriesColors array', () => { + const customSeriesColors = ['red', 'blue', 'green']; + const barSpec1 = MockSeriesSpec.bar({ id: specId1, data, customSeriesColors }); + const barSpec2 = MockSeriesSpec.bar({ id: specId2, data }); + const barSeriesSpecs = MockSeriesSpecs.fromSpecs([barSpec1, barSpec2]); + const barSeriesCollection = MockSeriesCollection.fromSpecs(barSeriesSpecs); + + it('it should return color from customSeriesColors array', () => { + const actual = getCustomSeriesColors(barSeriesSpecs, barSeriesCollection, new Map()); + + expect(actual.size).toBe(4); + barSeriesCollection.forEach(({ seriesIdentifier: { specId, key } }) => { + const color = actual.get(key); + if (specId === specId1) { + expect(customSeriesColors).toContainEqual(color); + } else { + expect(color).toBeUndefined(); + } + }); + }); + + it('it should return color from seriesColorOverrides', () => { + const actual = getCustomSeriesColors(barSeriesSpecs, barSeriesCollection, seriesColorOverrides); + + expect(actual.size).toBe(4); + barSeriesCollection.forEach(({ seriesIdentifier: { specId, key } }) => { + const color = actual.get(key); + if (key === targetKey) { + expect(color).toBe('blue'); + } else if (specId === specId1) { + expect(customSeriesColors).toContainEqual(color); + } else { + expect(color).toBeUndefined(); + } + }); + }); + }); + + describe('with customSeriesColors function', () => { + const customSeriesColors: SeriesColorAccessorFn = ({ yAccessor, splitAccessors }) => { + if (yAccessor === 'y' && splitAccessors.get('g') === 'b') { + return 'aquamarine'; + } + + return null; + }; + const barSpec1 = MockSeriesSpec.bar({ id: specId1, yAccessors: ['y'], data, customSeriesColors }); + const barSpec2 = MockSeriesSpec.bar({ id: specId2, data }); + const barSeriesSpecs = MockSeriesSpecs.fromSpecs([barSpec1, barSpec2]); + const barSeriesCollection = MockSeriesCollection.fromSpecs(barSeriesSpecs); + + it('it should return color from customSeriesColors function', () => { + const actual = getCustomSeriesColors(barSeriesSpecs, barSeriesCollection, new Map()); + + expect(actual.size).toBe(1); + expect(actual.get(targetKey)).toBe('aquamarine'); + }); + + it('it should return color from seriesColorOverrides', () => { + const actual = getCustomSeriesColors(barSeriesSpecs, barSeriesCollection, seriesColorOverrides); + + expect(actual.size).toBe(1); + expect(actual.get(targetKey)).toBe('blue'); + }); + }); + }); + }); + describe('Geometries counts', () => { test('can compute stacked geometries counts', () => { const area: AreaSeriesSpec = { @@ -374,8 +473,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -431,8 +530,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -490,8 +589,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -576,8 +675,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -649,8 +748,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -722,8 +821,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -803,8 +902,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -896,8 +995,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -982,8 +1081,8 @@ describe('Chart State utils', () => { }; const chartTheme = { ...LIGHT_THEME, colors: chartColors }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -1017,7 +1116,6 @@ describe('Chart State utils', () => { yScaleToDataExtent: false, data: BARCHART_1Y1G, }; - const bar1: BarSeriesSpec = { chartType: ChartTypes.XYAxis, specType: SpecTypes.Series, @@ -1048,8 +1146,8 @@ describe('Chart State utils', () => { }, }; const domainsByGroupId = mergeYCustomDomainsByGroupId(axesSpecs, chartRotation); - const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId); - const seriesColorMap = getSeriesColorMap(seriesDomains.seriesColors, chartColors, new Map()); + const seriesDomains = computeSeriesDomains(seriesSpecs, domainsByGroupId, undefined); + const seriesColorMap = getSeriesColors(seriesDomains.seriesCollection, chartColors, new Map()); const geometries = computeSeriesGeometries( seriesSpecs, seriesDomains.xDomain, @@ -1073,9 +1171,15 @@ describe('Chart State utils', () => { x: 0, y: 0, color: '#1EA593', - value: { x: 0, y: 5, accessor: AccessorType.Y1 }, + value: { x: 0, y: 5, accessor: BandedAccessorType.Y1 }, transform: { x: 0, y: 0 }, - geometryId: { specId: 'line1', seriesKey: [] }, + seriesIdentifier: { + specId: 'line1', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', + }, }, ]); const map2 = new Map(); @@ -1085,9 +1189,15 @@ describe('Chart State utils', () => { x: 0, y: 175.8, color: '#2B70F7', - value: { x: 0, y: 2, accessor: AccessorType.Y1 }, + value: { x: 0, y: 2, accessor: BandedAccessorType.Y1 }, transform: { x: 0, y: 0 }, - geometryId: { specId: 'line2', seriesKey: [] }, + seriesIdentifier: { + specId: 'line2', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', + }, }, ]); const merged = mergeGeometriesIndexes(map1, map2); @@ -1109,12 +1219,9 @@ describe('Chart State utils', () => { ); const histogramModeEnabled = true; const histogramModeDisabled = false; - expect(computeXScaleOffset(scale, histogramModeDisabled)).toBe(0); - // default alignment (start) expect(computeXScaleOffset(scale, histogramModeEnabled)).toBe(5); - expect(computeXScaleOffset(scale, histogramModeEnabled, HistogramModeAlignments.Center)).toBe(0); expect(computeXScaleOffset(scale, histogramModeEnabled, HistogramModeAlignments.End)).toBe(-5); }); @@ -1192,7 +1299,6 @@ describe('Chart State utils', () => { test('can set the bar series accessors dependent on histogram mode', () => { const isNotHistogramEnabled = false; const isHistogramEnabled = true; - const area: AreaSeriesSpec = { chartType: ChartTypes.XYAxis, specType: SpecTypes.Series, @@ -1237,21 +1343,16 @@ describe('Chart State utils', () => { yScaleToDataExtent: false, data: BARCHART_1Y1G, }; - const seriesMap = new Map([[area.id, area], [line.id, line]]); - // should not affect area or line series setBarSeriesAccessors(isHistogramEnabled, seriesMap); expect(seriesMap).toEqual(seriesMap); - // add bar series, histogram mode not enabled seriesMap.set(bar.id, bar); setBarSeriesAccessors(isNotHistogramEnabled, seriesMap); - // histogram mode setBarSeriesAccessors(isHistogramEnabled, seriesMap); expect(bar.stackAccessors).toEqual(['foo', 'g']); - // add another bar const bar2: BarSeriesSpec = { chartType: ChartTypes.XYAxis, @@ -1267,7 +1368,6 @@ describe('Chart State utils', () => { yScaleToDataExtent: false, data: BARCHART_1Y1G, }; - seriesMap.set(bar2.id, bar2); setBarSeriesAccessors(isHistogramEnabled, seriesMap); expect(bar2.stackAccessors).toEqual(['y', 'bar']); @@ -1278,7 +1378,13 @@ describe('Chart State utils', () => { key: 'specId:{bars},colors:{a}', color: '#1EA593', label: 'a', - value: { specId: 'bars', colorValues: ['a'], lastValue: { y0: null, y1: 6 } }, + seriesIdentifier: { + specId: 'bars', + seriesKeys: ['a'], + key: '', + splitAccessors: new Map(), + yAccessor: 'y1', + }, displayValue: { raw: { y0: null, y1: 6 }, formatted: { y0: null, y1: '6.00' } }, isSeriesVisible: false, }); @@ -1286,7 +1392,13 @@ describe('Chart State utils', () => { key: 'specId:{bars},colors:{b}', color: '#2B70F7', label: 'b', - value: { specId: 'bars', colorValues: ['b'], lastValue: { y0: null, y1: 2 } }, + seriesIdentifier: { + specId: 'bars', + seriesKeys: ['b'], + key: '', + splitAccessors: new Map(), + yAccessor: 'y1', + }, displayValue: { raw: { y0: null, y1: 2 }, formatted: { y0: null, y1: '2.00' } }, isSeriesVisible: false, }); @@ -1298,7 +1410,13 @@ describe('Chart State utils', () => { key: 'specId:{bars},colors:{a}', color: '#1EA593', label: 'a', - value: { specId: 'bars', colorValues: ['a'], lastValue: { y0: null, y1: 6 } }, + seriesIdentifier: { + specId: 'bars', + seriesKeys: ['a'], + key: '', + splitAccessors: new Map(), + yAccessor: 'y1', + }, displayValue: { raw: { y0: null, y1: 6 }, formatted: { y0: null, y1: '6.00' } }, isSeriesVisible: true, }); @@ -1306,7 +1424,13 @@ describe('Chart State utils', () => { key: 'specId:{bars},colors:{b}', color: '#2B70F7', label: 'b', - value: { specId: 'bars', colorValues: ['b'], lastValue: { y0: null, y1: 2 } }, + seriesIdentifier: { + specId: 'bars', + seriesKeys: ['b'], + key: '', + splitAccessors: new Map(), + yAccessor: 'y1', + }, displayValue: { raw: { y0: null, y1: 2 }, formatted: { y0: null, y1: '2.00' } }, isSeriesVisible: false, }); diff --git a/src/chart_types/xy_chart/state/utils.ts b/src/chart_types/xy_chart/state/utils.ts index 56c4a79b54..34b1bd9364 100644 --- a/src/chart_types/xy_chart/state/utils.ts +++ b/src/chart_types/xy_chart/state/utils.ts @@ -6,13 +6,14 @@ import { mutableIndexedGeometryMapUpsert, renderArea, renderBars, renderLine } f import { computeXScale, computeYScales, countBarsInCluster } from '../utils/scales'; import { DataSeries, - DataSeriesColorsValues, + SeriesCollectionValue, + getSeriesIndex, FormattedDataSeries, - getColorValuesAsString, getFormattedDataseries, getSplittedSeries, + getSeriesKey, + SeriesIdentifier, RawDataSeries, - findDataSeriesByColorValues, } from '../utils/series'; import { AreaSeriesSpec, @@ -91,33 +92,63 @@ export interface SeriesDomainsAndData { stacked: FormattedDataSeries[]; nonStacked: FormattedDataSeries[]; }; - seriesColors: Map; + seriesCollection: Map; } -export function updateDeselectedDataSeries( - series: DataSeriesColorsValues[] | null, - value: DataSeriesColorsValues, -): DataSeriesColorsValues[] { - const seriesIndex = findDataSeriesByColorValues(series, value); +/** + * Adds or removes series from array or series + * @param series + * @param target + */ +export function updateDeselectedDataSeries(series: SeriesIdentifier[], target: SeriesIdentifier): SeriesIdentifier[] { + const seriesIndex = getSeriesIndex(series, target); const updatedSeries = series ? [...series] : []; if (seriesIndex > -1) { updatedSeries.splice(seriesIndex, 1); } else { - updatedSeries.push(value); + updatedSeries.push(target); } return updatedSeries; } -export function getUpdatedCustomSeriesColors(seriesSpecs: BasicSeriesSpec[]): Map { - const updatedCustomSeriesColors = new Map(); - seriesSpecs.forEach((spec: BasicSeriesSpec) => { - if (spec.customSeriesColors) { - spec.customSeriesColors.forEach((color: string, seriesColorValues: DataSeriesColorsValues) => { - const { colorValues, specId } = seriesColorValues; - const seriesLabel = getColorValuesAsString(colorValues, specId); - updatedCustomSeriesColors.set(seriesLabel, color); - }); +/** + * Return map assocition between `seriesKey` and only the custom colors string + * @param seriesSpecs + * @param seriesCollection + * @param seriesColorOverrides color override from legend + */ +export function getCustomSeriesColors( + seriesSpecs: BasicSeriesSpec[], + seriesCollection: Map, + seriesColorOverrides: Map = new Map(), +): Map { + const updatedCustomSeriesColors = new Map(); + const counters = new Map(); + + seriesCollection.forEach(({ seriesIdentifier }, seriesKey) => { + const spec = getSpecsById(seriesSpecs, seriesIdentifier.specId); + + if (!spec || !(spec.customSeriesColors || seriesColorOverrides.size > 0)) { + return; + } + + let color: string | undefined | null; + + if (seriesColorOverrides.has(seriesKey)) { + color = seriesColorOverrides.get(seriesKey); + } + + if (!color && spec.customSeriesColors) { + const counter = counters.get(seriesIdentifier.specId) || 0; + color = Array.isArray(spec.customSeriesColors) + ? spec.customSeriesColors[counter % spec.customSeriesColors.length] + : spec.customSeriesColors(seriesIdentifier); + counters.set(seriesIdentifier.specId, counter + 1); + } + + if (color) { + updatedCustomSeriesColors.set(seriesKey, color); } }); return updatedCustomSeriesColors; @@ -137,13 +168,14 @@ function getLastValues(formattedDataSeries: { // we need to get the latest formattedDataSeries.stacked.forEach((ds) => { ds.dataSeries.forEach((series) => { + const seriesKey = getSeriesKey(series as SeriesIdentifier); if (series.data.length > 0) { const last = series.data[series.data.length - 1]; if (last !== null) { const { initialY1: y1, initialY0: y0 } = last; if (!last.filled && (y1 !== null || y0 !== null)) { - lastValues.set(series.seriesColorKey, { y0, y1 }); + lastValues.set(seriesKey, { y0, y1 }); } } } @@ -151,12 +183,13 @@ function getLastValues(formattedDataSeries: { }); formattedDataSeries.nonStacked.forEach((ds) => { ds.dataSeries.forEach((series) => { + const seriesKey = getSeriesKey(series as SeriesIdentifier); if (series.data.length > 0) { const last = series.data[series.data.length - 1]; if (last !== null) { const { initialY1: y1, initialY0: y0 } = last; if (y1 !== null || y0 !== null) { - lastValues.set(series.seriesColorKey, { y0, y1 }); + lastValues.set(seriesKey, { y0, y1 }); } } } @@ -178,11 +211,12 @@ function getLastValues(formattedDataSeries: { export function computeSeriesDomains( seriesSpecs: BasicSeriesSpec[], customYDomainsByGroupId: Map = new Map(), - deselectedDataSeries: DataSeriesColorsValues[] = [], + deselectedDataSeries: SeriesIdentifier[] = [], customXDomain?: DomainRange | Domain, ): SeriesDomainsAndData { - const { splittedSeries, xValues, seriesColors } = getSplittedSeries(seriesSpecs, deselectedDataSeries); - + const { splittedSeries, xValues, seriesCollection } = deselectedDataSeries + ? getSplittedSeries(seriesSpecs, deselectedDataSeries) + : getSplittedSeries(seriesSpecs, []); const splittedDataSeries = [...splittedSeries.values()]; const specsArray = [...seriesSpecs.values()]; @@ -199,22 +233,22 @@ export function computeSeriesDomains( // we need to get the last values from the formatted dataseries // because we change the format if we are on percentage mode - const lastValuesMap = getLastValues(formattedDataSeries); - const updatedSeriesColors = new Map(); - seriesColors.forEach((value, key) => { - const lastValue = lastValuesMap.get(key); - const updatedColorSet: DataSeriesColorsValues = { + const lastValues = getLastValues(formattedDataSeries); + const updatedSeriesCollection = new Map(); + seriesCollection.forEach((value, key) => { + const lastValue = lastValues.get(key); + const updatedColorSet: SeriesCollectionValue = { ...value, lastValue, }; - updatedSeriesColors.set(key, updatedColorSet); + updatedSeriesCollection.set(key, updatedColorSet); }); return { xDomain, yDomain, splittedDataSeries, formattedDataSeries, - seriesColors: updatedSeriesColors, + seriesCollection: updatedSeriesCollection, }; } @@ -450,7 +484,8 @@ function renderGeometries( if (spec === undefined) { continue; } - const color = seriesColorsMap.get(ds.seriesColorKey) || defaultColor; + + const color = seriesColorsMap.get(getSeriesKey(ds)) || defaultColor; if (isBarSeriesSpec(spec)) { const shift = isStacked ? indexOffset : indexOffset + barIndexOffset; @@ -470,12 +505,10 @@ function renderGeometries( const renderedBars = renderBars( shift, - ds.data, + ds, xScale, yScale, color, - ds.specId, - ds.key, barSeriesStyle, displayValueSettings, spec.styleAccessor, @@ -496,14 +529,12 @@ function renderGeometries( const renderedLines = renderLine( // move the point on half of the bandwidth if we have mixed bars/lines (xScale.bandwidth * lineShift) / 2, - ds.data, + ds, xScale, yScale, color, (spec as LineSeriesSpec).curve || CurveType.LINEAR, - ds.specId, isBandedSpec(spec.y0Accessors), - ds.key, xScaleOffset, lineSeriesStyle, spec.pointStyleAccessor, @@ -523,14 +554,12 @@ function renderGeometries( const renderedAreas = renderArea( // move the point on half of the bandwidth if we have mixed bars/lines (xScale.bandwidth * areaShift) / 2, - ds.data, + ds, xScale, yScale, color, (spec as AreaSeriesSpec).curve || CurveType.LINEAR, - ds.specId, isBandedSpec(spec.y0Accessors), - ds.key, xScaleOffset, areaSeriesStyle, isStacked, diff --git a/src/chart_types/xy_chart/tooltip/tooltip.test.ts b/src/chart_types/xy_chart/tooltip/tooltip.test.ts index dd79b82f80..7c5b6e8d3e 100644 --- a/src/chart_types/xy_chart/tooltip/tooltip.test.ts +++ b/src/chart_types/xy_chart/tooltip/tooltip.test.ts @@ -60,9 +60,12 @@ describe('Tooltip formatting', () => { width: 0, height: 0, color: 'blue', - geometryId: { + seriesIdentifier: { specId: SPEC_ID_1, - seriesKey: [], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], }, value: { x: 1, @@ -77,9 +80,12 @@ describe('Tooltip formatting', () => { width: 0, height: 0, color: 'blue', - geometryId: { + seriesIdentifier: { specId: SPEC_ID_1, - seriesKey: [], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], }, value: { x: 1, @@ -90,7 +96,7 @@ describe('Tooltip formatting', () => { }; test('format simple tooltip', () => { - const tooltipValue = formatTooltip(indexedGeometry, SPEC_1, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltip(indexedGeometry, SPEC_1, false, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.yAccessor).toBe('y1'); expect(tooltipValue.name).toBe('bar_1'); @@ -101,15 +107,15 @@ describe('Tooltip formatting', () => { }); it('should set name as spec name when provided', () => { const name = 'test - spec'; - const tooltipValue = formatTooltip(indexedBandedGeometry, { ...SPEC_1, name }, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltip(indexedBandedGeometry, { ...SPEC_1, name }, false, false, false, YAXIS_SPEC); expect(tooltipValue.name).toBe(name); }); it('should set name as spec id when name is not provided', () => { - const tooltipValue = formatTooltip(indexedBandedGeometry, SPEC_1, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltip(indexedBandedGeometry, SPEC_1, false, false, false, YAXIS_SPEC); expect(tooltipValue.name).toBe(SPEC_1.id); }); test('format banded tooltip - upper', () => { - const tooltipValue = formatTooltip(indexedBandedGeometry, bandedSpec, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltip(indexedBandedGeometry, bandedSpec, false, false, false, YAXIS_SPEC); expect(tooltipValue.name).toBe('bar_1 - upper'); }); test('format banded tooltip - y1AccessorFormat', () => { @@ -118,6 +124,7 @@ describe('Tooltip formatting', () => { { ...bandedSpec, y1AccessorFormat: ' [max]' }, false, false, + false, YAXIS_SPEC, ); expect(tooltipValue.name).toBe('bar_1 [max]'); @@ -128,6 +135,7 @@ describe('Tooltip formatting', () => { { ...bandedSpec, y1AccessorFormat: (label) => `[max] ${label}` }, false, false, + false, YAXIS_SPEC, ); expect(tooltipValue.name).toBe('[max] bar_1'); @@ -144,6 +152,7 @@ describe('Tooltip formatting', () => { bandedSpec, false, false, + false, YAXIS_SPEC, ); expect(tooltipValue.name).toBe('bar_1 - lower'); @@ -160,6 +169,7 @@ describe('Tooltip formatting', () => { { ...bandedSpec, y0AccessorFormat: ' [min]' }, false, false, + false, YAXIS_SPEC, ); expect(tooltipValue.name).toBe('bar_1 [min]'); @@ -176,22 +186,26 @@ describe('Tooltip formatting', () => { { ...bandedSpec, y0AccessorFormat: (label) => `[min] ${label}` }, false, false, + false, YAXIS_SPEC, ); expect(tooltipValue.name).toBe('[min] bar_1'); }); - test('format tooltip with seriesKey name', () => { + test('format tooltip with seriesKeys name', () => { const geometry: BarGeometry = { ...indexedGeometry, - geometryId: { + seriesIdentifier: { specId: SPEC_ID_1, - seriesKey: ['y1'], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['y1'], }, }; - const tooltipValue = formatTooltip(geometry, SPEC_1, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltip(geometry, SPEC_1, false, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.yAccessor).toBe('y1'); - expect(tooltipValue.name).toBe('y1'); + expect(tooltipValue.name).toBe('bar_1'); expect(tooltipValue.isXValue).toBe(false); expect(tooltipValue.isHighlighted).toBe(false); expect(tooltipValue.color).toBe('blue'); @@ -205,7 +219,7 @@ describe('Tooltip formatting', () => { accessor: 'y0', }, }; - const tooltipValue = formatTooltip(geometry, SPEC_1, false, false, YAXIS_SPEC); + const tooltipValue = formatTooltip(geometry, SPEC_1, false, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.yAccessor).toBe('y0'); expect(tooltipValue.name).toBe('bar_1'); @@ -222,7 +236,7 @@ describe('Tooltip formatting', () => { accessor: 'y0', }, }; - let tooltipValue = formatTooltip(geometry, SPEC_1, true, false, YAXIS_SPEC); + let tooltipValue = formatTooltip(geometry, SPEC_1, true, false, false, YAXIS_SPEC); expect(tooltipValue).toBeDefined(); expect(tooltipValue.yAccessor).toBe('y0'); expect(tooltipValue.name).toBe('bar_1'); @@ -231,7 +245,7 @@ describe('Tooltip formatting', () => { expect(tooltipValue.color).toBe('blue'); expect(tooltipValue.value).toBe('1'); // disable any highlight on x value - tooltipValue = formatTooltip(geometry, SPEC_1, true, true, YAXIS_SPEC); + tooltipValue = formatTooltip(geometry, SPEC_1, true, true, false, YAXIS_SPEC); expect(tooltipValue.isHighlighted).toBe(false); }); }); diff --git a/src/chart_types/xy_chart/tooltip/tooltip.ts b/src/chart_types/xy_chart/tooltip/tooltip.ts index 45e2b0e32d..b8b1d3f285 100644 --- a/src/chart_types/xy_chart/tooltip/tooltip.ts +++ b/src/chart_types/xy_chart/tooltip/tooltip.ts @@ -1,5 +1,4 @@ import { TooltipValue } from '../utils/interactions'; -import { getColorValuesAsString } from '../utils/series'; import { AxisSpec, BasicSeriesSpec, @@ -8,8 +7,9 @@ import { isBarSeriesSpec, TickFormatterOptions, } from '../utils/specs'; -import { IndexedGeometry, AccessorType } from '../../../utils/geometry'; +import { IndexedGeometry, BandedAccessorType } from '../../../utils/geometry'; import { getAccessorFormatLabel } from '../../../utils/accessor'; +import { getSeriesKey, getSeriesLabel } from '../utils/series'; export interface TooltipLegendValue { y0: any; @@ -42,41 +42,39 @@ export function getSeriesTooltipValues( [yAccessor]: seriesValue, }); }); - return seriesTooltipValues; } export function formatTooltip( - { color, value: { x, y, accessor }, geometryId: { seriesKey } }: IndexedGeometry, + { color, value: { x, y, accessor }, seriesIdentifier }: IndexedGeometry, spec: BasicSeriesSpec, isXValue: boolean, isHighlighted: boolean, + hasSingleSeries: boolean, axisSpec?: AxisSpec, ): TooltipValue { - const seriesKeyAsString = getColorValuesAsString(seriesKey, spec.id); - let displayName: string | undefined; - if (seriesKey.length > 0) { - displayName = seriesKey.join(' - '); - } else { - displayName = spec.name || `${spec.id}`; - } + const seriesKey = getSeriesKey(seriesIdentifier); + let displayName = getSeriesLabel(seriesIdentifier, hasSingleSeries, true, spec); if (isBandedSpec(spec.y0Accessors) && (isAreaSeriesSpec(spec) || isBarSeriesSpec(spec))) { const { y0AccessorFormat = Y0_ACCESSOR_POSTFIX, y1AccessorFormat = Y1_ACCESSOR_POSTFIX } = spec; - const formatter = accessor === AccessorType.Y0 ? y0AccessorFormat : y1AccessorFormat; + const formatter = accessor === BandedAccessorType.Y0 ? y0AccessorFormat : y1AccessorFormat; displayName = getAccessorFormatLabel(formatter, displayName); } + const isFiltered = spec.filterSeriesInTooltip !== undefined ? spec.filterSeriesInTooltip(seriesIdentifier) : true; + const isVisible = displayName === '' ? false : isFiltered; const value = isXValue ? x : y; const tickFormatOptions: TickFormatterOptions | undefined = spec.timeZone ? { timeZone: spec.timeZone } : undefined; return { - seriesKey: seriesKeyAsString, + seriesKey, name: displayName, value: axisSpec ? axisSpec.tickFormat(value, tickFormatOptions) : emptyFormatter(value), color, isHighlighted: isXValue ? false : isHighlighted, isXValue, yAccessor: accessor, + isVisible, }; } diff --git a/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap b/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap index e383d6b4c4..2c6ab09efd 100644 --- a/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap +++ b/src/chart_types/xy_chart/utils/__snapshots__/series.test.ts.snap @@ -11,8 +11,22 @@ Array [ }, "x": 0, "y0": null, - "y1": 1, + "y1": undefined, }, + ], + "key": "spec{spec1}yAccessor{y1}splitAccessors{y-1}", + "seriesKeys": Array [ + 1, + "y1", + ], + "specId": "spec1", + "splitAccessors": Map { + "y" => 1, + }, + "yAccessor": "y1", + }, + Object { + "data": Array [ Object { "datum": Object { "x": 1, @@ -20,8 +34,22 @@ Array [ }, "x": 1, "y0": null, - "y1": 2, + "y1": undefined, }, + ], + "key": "spec{spec1}yAccessor{y1}splitAccessors{y-2}", + "seriesKeys": Array [ + 2, + "y1", + ], + "specId": "spec1", + "splitAccessors": Map { + "y" => 2, + }, + "yAccessor": "y1", + }, + Object { + "data": Array [ Object { "datum": Object { "x": 2, @@ -29,8 +57,22 @@ Array [ }, "x": 2, "y0": null, - "y1": 10, + "y1": undefined, }, + ], + "key": "spec{spec1}yAccessor{y1}splitAccessors{y-10}", + "seriesKeys": Array [ + 10, + "y1", + ], + "specId": "spec1", + "splitAccessors": Map { + "y" => 10, + }, + "yAccessor": "y1", + }, + Object { + "data": Array [ Object { "datum": Object { "x": 3, @@ -38,12 +80,19 @@ Array [ }, "x": 3, "y0": null, - "y1": 6, + "y1": undefined, }, ], - "key": Array [], - "seriesColorKey": "specId:{spec1},colors:{}", + "key": "spec{spec1}yAccessor{y1}splitAccessors{y-6}", + "seriesKeys": Array [ + 6, + "y1", + ], "specId": "spec1", + "splitAccessors": Map { + "y" => 6, + }, + "yAccessor": "y1", }, ] `; @@ -64,60 +113,41 @@ Array [ }, Object { "datum": Object { - "g": "a", - "x": 1, + "g": "b", + "x": 0, "y": 2, }, - "x": 1, + "x": 0, "y0": null, "y1": 2, }, Object { "datum": Object { "g": "a", - "x": 2, - "y": 3, - }, - "x": 2, - "y0": null, - "y1": 3, - }, - Object { - "datum": Object { - "g": "a", - "x": 3, - "y": 4, + "x": 1, + "y": 2, }, - "x": 3, + "x": 1, "y0": null, - "y1": 4, + "y1": 2, }, - ], - "key": Array [ - "a", - ], - "seriesColorKey": "specId:{spec1},colors:{a}", - "specId": "spec1", - }, - Object { - "data": Array [ Object { "datum": Object { "g": "b", - "x": 0, - "y": 2, + "x": 1, + "y": 3, }, - "x": 0, + "x": 1, "y0": null, - "y1": 2, + "y1": 3, }, Object { "datum": Object { - "g": "b", - "x": 1, + "g": "a", + "x": 2, "y": 3, }, - "x": 1, + "x": 2, "y0": null, "y1": 3, }, @@ -131,6 +161,16 @@ Array [ "y0": null, "y1": 4, }, + Object { + "datum": Object { + "g": "a", + "x": 3, + "y": 4, + }, + "x": 3, + "y0": null, + "y1": 4, + }, Object { "datum": Object { "g": "b", @@ -142,11 +182,13 @@ Array [ "y1": 5, }, ], - "key": Array [ - "b", + "key": "spec{spec1}yAccessor{y}splitAccessors{}", + "seriesKeys": Array [ + "y", ], - "seriesColorKey": "specId:{spec1},colors:{b}", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y", }, ] `; @@ -200,12 +242,18 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y}splitAccessors{g1-a|g2-s}", + "seriesKeys": Array [ "a", "s", + "y", ], - "seriesColorKey": "specId:{spec1},colors:{a,s}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "a", + "g2" => "s", + }, + "yAccessor": "y", }, Object { "data": Array [ @@ -254,12 +302,18 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y}splitAccessors{g1-a|g2-p}", + "seriesKeys": Array [ "a", "p", + "y", ], - "seriesColorKey": "specId:{spec1},colors:{a,p}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "a", + "g2" => "p", + }, + "yAccessor": "y", }, Object { "data": Array [ @@ -308,12 +362,18 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y}splitAccessors{g1-b|g2-s}", + "seriesKeys": Array [ "b", "s", + "y", ], - "seriesColorKey": "specId:{spec1},colors:{b,s}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "b", + "g2" => "s", + }, + "yAccessor": "y", }, Object { "data": Array [ @@ -362,12 +422,18 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y}splitAccessors{g1-b|g2-p}", + "seriesKeys": Array [ "b", "p", + "y", ], - "seriesColorKey": "specId:{spec1},colors:{b,p}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "b", + "g2" => "p", + }, + "yAccessor": "y", }, ] `; @@ -417,11 +483,13 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{}", + "seriesKeys": Array [ "y1", ], - "seriesColorKey": "specId:{spec1},colors:{y1}", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -466,11 +534,13 @@ Array [ "y1": 10, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{}", + "seriesKeys": Array [ "y2", ], - "seriesColorKey": "specId:{spec1},colors:{y2}", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y2", }, ] `; @@ -524,12 +594,16 @@ Array [ "y1": 7, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{g-a}", + "seriesKeys": Array [ "a", "y1", ], - "seriesColorKey": "specId:{spec1},colors:{a,y1}", "specId": "spec1", + "splitAccessors": Map { + "g" => "a", + }, + "yAccessor": "y1", }, Object { "data": Array [ @@ -578,12 +652,16 @@ Array [ "y1": 3, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{g-a}", + "seriesKeys": Array [ "a", "y2", ], - "seriesColorKey": "specId:{spec1},colors:{a,y2}", "specId": "spec1", + "splitAccessors": Map { + "g" => "a", + }, + "yAccessor": "y2", }, Object { "data": Array [ @@ -632,12 +710,16 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{g-b}", + "seriesKeys": Array [ "b", "y1", ], - "seriesColorKey": "specId:{spec1},colors:{b,y1}", "specId": "spec1", + "splitAccessors": Map { + "g" => "b", + }, + "yAccessor": "y1", }, Object { "data": Array [ @@ -686,12 +768,16 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{g-b}", + "seriesKeys": Array [ "b", "y2", ], - "seriesColorKey": "specId:{spec1},colors:{b,y2}", "specId": "spec1", + "splitAccessors": Map { + "g" => "b", + }, + "yAccessor": "y2", }, ] `; @@ -761,13 +847,18 @@ Array [ "y1": 7, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{g1-cdn.google.com|g2-direct-cdn}", + "seriesKeys": Array [ "cdn.google.com", "direct-cdn", "y1", ], - "seriesColorKey": "specId:{spec1},colors:{cdn.google.com,direct-cdn,y1}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cdn.google.com", + "g2" => "direct-cdn", + }, + "yAccessor": "y1", }, Object { "data": Array [ @@ -832,13 +923,18 @@ Array [ "y1": 3, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{g1-cdn.google.com|g2-direct-cdn}", + "seriesKeys": Array [ "cdn.google.com", "direct-cdn", "y2", ], - "seriesColorKey": "specId:{spec1},colors:{cdn.google.com,direct-cdn,y2}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cdn.google.com", + "g2" => "direct-cdn", + }, + "yAccessor": "y2", }, Object { "data": Array [ @@ -903,13 +999,18 @@ Array [ "y1": 7, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{g1-cdn.google.com|g2-indirect-cdn}", + "seriesKeys": Array [ "cdn.google.com", "indirect-cdn", "y1", ], - "seriesColorKey": "specId:{spec1},colors:{cdn.google.com,indirect-cdn,y1}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cdn.google.com", + "g2" => "indirect-cdn", + }, + "yAccessor": "y1", }, Object { "data": Array [ @@ -974,13 +1075,18 @@ Array [ "y1": 3, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{g1-cdn.google.com|g2-indirect-cdn}", + "seriesKeys": Array [ "cdn.google.com", "indirect-cdn", "y2", ], - "seriesColorKey": "specId:{spec1},colors:{cdn.google.com,indirect-cdn,y2}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cdn.google.com", + "g2" => "indirect-cdn", + }, + "yAccessor": "y2", }, Object { "data": Array [ @@ -1045,13 +1151,18 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{g1-cloudflare.com|g2-direct-cdn}", + "seriesKeys": Array [ "cloudflare.com", "direct-cdn", "y1", ], - "seriesColorKey": "specId:{spec1},colors:{cloudflare.com,direct-cdn,y1}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cloudflare.com", + "g2" => "direct-cdn", + }, + "yAccessor": "y1", }, Object { "data": Array [ @@ -1116,13 +1227,18 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{g1-cloudflare.com|g2-direct-cdn}", + "seriesKeys": Array [ "cloudflare.com", "direct-cdn", "y2", ], - "seriesColorKey": "specId:{spec1},colors:{cloudflare.com,direct-cdn,y2}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cloudflare.com", + "g2" => "direct-cdn", + }, + "yAccessor": "y2", }, Object { "data": Array [ @@ -1187,13 +1303,18 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y1}splitAccessors{g1-cloudflare.com|g2-indirect-cdn}", + "seriesKeys": Array [ "cloudflare.com", "indirect-cdn", "y1", ], - "seriesColorKey": "specId:{spec1},colors:{cloudflare.com,indirect-cdn,y1}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cloudflare.com", + "g2" => "indirect-cdn", + }, + "yAccessor": "y1", }, Object { "data": Array [ @@ -1258,13 +1379,18 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "spec{spec1}yAccessor{y2}splitAccessors{g1-cloudflare.com|g2-indirect-cdn}", + "seriesKeys": Array [ "cloudflare.com", "indirect-cdn", "y2", ], - "seriesColorKey": "specId:{spec1},colors:{cloudflare.com,indirect-cdn,y2}", "specId": "spec1", + "splitAccessors": Map { + "g1" => "cloudflare.com", + "g2" => "indirect-cdn", + }, + "yAccessor": "y2", }, ] `; @@ -9274,11 +9400,13 @@ Array [ "y1": 999, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17283,11 +17411,13 @@ Array [ "y1": 1998, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -17329,11 +17459,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17370,11 +17502,13 @@ Array [ "y1": 8, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17411,11 +17545,13 @@ Array [ "y1": 12, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17452,11 +17588,13 @@ Array [ "y1": 16, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -17498,11 +17636,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17539,11 +17679,13 @@ Array [ "y1": 8, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17580,11 +17722,13 @@ Array [ "y1": 12, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17621,11 +17765,13 @@ Array [ "y1": 16, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -17671,11 +17817,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17720,11 +17868,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -17770,11 +17920,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17819,11 +17971,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -17869,11 +18023,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -17910,11 +18066,13 @@ Array [ "y1": 8, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -17960,11 +18118,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -18001,11 +18161,13 @@ Array [ "y1": 8, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -18051,11 +18213,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "a", + "seriesKeys": Array [ "a", ], - "seriesColorKey": "a", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -18100,11 +18264,13 @@ Array [ "y1": 4, }, ], - "key": Array [ + "key": "b", + "seriesKeys": Array [ "b", ], - "seriesColorKey": "b", "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y1", }, ] `; @@ -18169,11 +18335,13 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec2}yAccessor{y1}splitAccessors{}", + "seriesKeys": Array [ "y1", ], - "seriesColorKey": "specId:{spec2},colors:{y1}", "specId": "spec2", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -18226,11 +18394,13 @@ Array [ "y1": 16, }, ], - "key": Array [ + "key": "spec{spec2}yAccessor{y2}splitAccessors{}", + "seriesKeys": Array [ "y2", ], - "seriesColorKey": "specId:{spec2},colors:{y2}", "specId": "spec2", + "splitAccessors": Map {}, + "yAccessor": "y2", }, ], "groupId": "group2", @@ -18279,9 +18449,13 @@ Array [ "y1": 6, }, ], - "key": Array [], - "seriesColorKey": "specId:{spec1},colors:{}", + "key": "spec{spec1}yAccessor{y}splitAccessors{}", + "seriesKeys": Array [ + "y", + ], "specId": "spec1", + "splitAccessors": Map {}, + "yAccessor": "y", }, ] `; @@ -18331,11 +18505,13 @@ Array [ "y1": 6, }, ], - "key": Array [ + "key": "spec{spec2}yAccessor{y1}splitAccessors{}", + "seriesKeys": Array [ "y1", ], - "seriesColorKey": "specId:{spec2},colors:{y1}", "specId": "spec2", + "splitAccessors": Map {}, + "yAccessor": "y1", }, Object { "data": Array [ @@ -18380,11 +18556,13 @@ Array [ "y1": 10, }, ], - "key": Array [ + "key": "spec{spec2}yAccessor{y2}splitAccessors{}", + "seriesKeys": Array [ "y2", ], - "seriesColorKey": "specId:{spec2},colors:{y2}", "specId": "spec2", + "splitAccessors": Map {}, + "yAccessor": "y2", }, ] `; diff --git a/src/chart_types/xy_chart/utils/interactions.test.ts b/src/chart_types/xy_chart/utils/interactions.test.ts index e8e323dbbb..355407a2b4 100644 --- a/src/chart_types/xy_chart/utils/interactions.test.ts +++ b/src/chart_types/xy_chart/utils/interactions.test.ts @@ -30,9 +30,12 @@ const seriesStyle = { const ig1: IndexedGeometry = { color: 'red', - geometryId: { + seriesIdentifier: { specId: 'ig1', - seriesKey: [0, 1, 2], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [0, 1, 2], }, value: { accessor: 'y1', @@ -46,9 +49,12 @@ const ig1: IndexedGeometry = { seriesStyle, }; const ig2: IndexedGeometry = { - geometryId: { + seriesIdentifier: { specId: 'ig1', - seriesKey: [0, 1, 2], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [0, 1, 2], }, value: { accessor: 'y1', @@ -63,9 +69,12 @@ const ig2: IndexedGeometry = { seriesStyle, }; const ig3: IndexedGeometry = { - geometryId: { + seriesIdentifier: { specId: 'ig1', - seriesKey: [123, 123, 123], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [123, 123, 123], }, value: { accessor: 'y1', @@ -81,9 +90,12 @@ const ig3: IndexedGeometry = { seriesStyle, }; const ig4: IndexedGeometry = { - geometryId: { + seriesIdentifier: { specId: 'ig4', - seriesKey: [123, 123, 123], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [123, 123, 123], }, value: { accessor: 'y1', @@ -98,9 +110,12 @@ const ig4: IndexedGeometry = { seriesStyle, }; const ig5: IndexedGeometry = { - geometryId: { + seriesIdentifier: { specId: 'ig5', - seriesKey: [123, 123, 123], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [123, 123, 123], }, value: { accessor: 'y1', @@ -115,9 +130,12 @@ const ig5: IndexedGeometry = { seriesStyle, }; const ig6: PointGeometry = { - geometryId: { + seriesIdentifier: { specId: 'ig5', - seriesKey: [123, 123, 123], + key: '', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [123, 123, 123], }, value: { accessor: 'y1', diff --git a/src/chart_types/xy_chart/utils/interactions.ts b/src/chart_types/xy_chart/utils/interactions.ts index 39aac2294a..b852619f08 100644 --- a/src/chart_types/xy_chart/utils/interactions.ts +++ b/src/chart_types/xy_chart/utils/interactions.ts @@ -26,6 +26,7 @@ export interface TooltipValue { isXValue: boolean; seriesKey: string; yAccessor: Accessor; + isVisible: boolean; } export interface TooltipProps { @@ -107,7 +108,7 @@ export function areIndexedGeomsEquals(ig1: IndexedGeometry, ig2: IndexedGeometry function arePointsEqual(ig1: PointGeometry, ig2: PointGeometry) { return ( - ig1.geometryId.specId === ig2.geometryId.specId && + ig1.seriesIdentifier.specId === ig2.seriesIdentifier.specId && ig1.color === ig2.color && ig1.x === ig2.x && ig1.transform.x === ig2.transform.x && @@ -118,7 +119,7 @@ function arePointsEqual(ig1: PointGeometry, ig2: PointGeometry) { } function areBarEqual(ig1: BarGeometry, ig2: BarGeometry) { return ( - ig1.geometryId.specId === ig2.geometryId.specId && + ig1.seriesIdentifier.specId === ig2.seriesIdentifier.specId && ig1.color === ig2.color && ig1.x === ig2.x && ig1.y === ig2.y && diff --git a/src/chart_types/xy_chart/utils/nonstacked_series_utils.test.ts b/src/chart_types/xy_chart/utils/nonstacked_series_utils.test.ts index aa05e56385..c016e82977 100644 --- a/src/chart_types/xy_chart/utils/nonstacked_series_utils.test.ts +++ b/src/chart_types/xy_chart/utils/nonstacked_series_utils.test.ts @@ -10,8 +10,10 @@ import { Fit } from './specs'; const EMPTY_DATA_SET: RawDataSeries[] = [ { data: [], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec1', }, ]; @@ -23,8 +25,10 @@ const STANDARD_DATA_SET: RawDataSeries[] = [ y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec1', }, { @@ -34,8 +38,10 @@ const STANDARD_DATA_SET: RawDataSeries[] = [ y1: 20, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec2', }, { @@ -45,8 +51,10 @@ const STANDARD_DATA_SET: RawDataSeries[] = [ y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec3', }, ]; @@ -58,8 +66,10 @@ const WITH_NULL_DATASET: RawDataSeries[] = [ y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec1', }, { @@ -69,8 +79,10 @@ const WITH_NULL_DATASET: RawDataSeries[] = [ y1: null, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec2', }, { @@ -80,8 +92,10 @@ const WITH_NULL_DATASET: RawDataSeries[] = [ y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec3', }, ]; @@ -94,8 +108,10 @@ const STANDARD_DATA_SET_WY0: RawDataSeries[] = [ y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec1', }, { @@ -106,8 +122,10 @@ const STANDARD_DATA_SET_WY0: RawDataSeries[] = [ y1: 20, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec2', }, { @@ -118,8 +136,10 @@ const STANDARD_DATA_SET_WY0: RawDataSeries[] = [ y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec3', }, ]; @@ -132,8 +152,10 @@ const WITH_NULL_DATASET_WY0: RawDataSeries[] = [ y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec1', }, { @@ -143,8 +165,10 @@ const WITH_NULL_DATASET_WY0: RawDataSeries[] = [ y1: null, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec2', }, { @@ -155,22 +179,28 @@ const WITH_NULL_DATASET_WY0: RawDataSeries[] = [ y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + seriesKeys: [], + yAccessor: 'y1', + splitAccessors: new Map(), + key: 'color-key', specId: 'spec3', }, ]; const DATA_SET_WITH_NULL_2: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 21 }, { x: 3, y1: 23 }], }, ]; diff --git a/src/chart_types/xy_chart/utils/nonstacked_series_utils.ts b/src/chart_types/xy_chart/utils/nonstacked_series_utils.ts index 2c3980c8e2..a9e57dd946 100644 --- a/src/chart_types/xy_chart/utils/nonstacked_series_utils.ts +++ b/src/chart_types/xy_chart/utils/nonstacked_series_utils.ts @@ -34,9 +34,7 @@ export const formatNonStackedDataSeriesValues = ( export const formatNonStackedDataValues = (dataSeries: RawDataSeries, scaleToExtent: boolean): DataSeries => { const len = dataSeries.data.length; const formattedValues: DataSeries = { - key: dataSeries.key, - specId: dataSeries.specId, - seriesColorKey: dataSeries.seriesColorKey, + ...dataSeries, data: [], }; for (let i = 0; i < len; i++) { diff --git a/src/chart_types/xy_chart/utils/series.test.ts b/src/chart_types/xy_chart/utils/series.test.ts index 1185c058ca..8ad65d0cdb 100644 --- a/src/chart_types/xy_chart/utils/series.test.ts +++ b/src/chart_types/xy_chart/utils/series.test.ts @@ -1,13 +1,14 @@ import { ColorConfig } from '../../../utils/themes/theme'; import { ScaleType } from '../../../utils/scales/scales'; import { - DataSeriesColorsValues, + SeriesCollectionValue, getFormattedDataseries, - getSeriesColorMap, + getSeriesColors, getSortedDataSeriesColorsValuesMap, getSplittedSeries, RawDataSeries, splitSeries, + SeriesIdentifier, } from './series'; import { BasicSeriesSpec, LineSeriesSpec, SpecTypes, SeriesTypes } from './specs'; import { formatStackedDataSeriesValues } from './stacked_series_utils'; @@ -16,87 +17,80 @@ import { ChartTypes } from '../..'; describe('Series', () => { test('Can split dataset into 1Y0G series', () => { - const splittedSeries = splitSeries( - TestDataset.BARCHART_1Y0G, - { - xAccessor: 'x', - yAccessors: ['y'], - }, - 'spec1', - ); + const splittedSeries = splitSeries({ + id: 'spec1', + data: TestDataset.BARCHART_1Y0G, + xAccessor: 'x', + yAccessors: ['y1'], + splitSeriesAccessors: ['y'], + }); + expect(splittedSeries.rawDataSeries).toMatchSnapshot(); }); test('Can split dataset into 1Y1G series', () => { - const splittedSeries = splitSeries( - TestDataset.BARCHART_1Y1G, - { - xAccessor: 'x', - yAccessors: ['y'], - splitSeriesAccessors: ['g'], - }, - 'spec1', - ); + const splittedSeries = splitSeries({ + id: 'spec1', + data: TestDataset.BARCHART_1Y1G, + xAccessor: 'x', + yAccessors: ['y'], + }); expect(splittedSeries.rawDataSeries).toMatchSnapshot(); }); test('Can split dataset into 1Y2G series', () => { - const splittedSeries = splitSeries( - TestDataset.BARCHART_1Y2G, - { - xAccessor: 'x', - yAccessors: ['y'], - splitSeriesAccessors: ['g1', 'g2'], - }, - 'spec1', - ); + const splittedSeries = splitSeries({ + id: 'spec1', + data: TestDataset.BARCHART_1Y2G, + xAccessor: 'x', + yAccessors: ['y'], + splitSeriesAccessors: ['g1', 'g2'], + }); expect(splittedSeries.rawDataSeries).toMatchSnapshot(); }); test('Can split dataset into 2Y0G series', () => { - const splittedSeries = splitSeries( - TestDataset.BARCHART_2Y0G, - { - xAccessor: 'x', - yAccessors: ['y1', 'y2'], - }, - 'spec1', - ); + const splittedSeries = splitSeries({ + id: 'spec1', + data: TestDataset.BARCHART_2Y0G, + xAccessor: 'x', + yAccessors: ['y1', 'y2'], + }); expect(splittedSeries.rawDataSeries).toMatchSnapshot(); }); test('Can split dataset into 2Y1G series', () => { - const splittedSeries = splitSeries( - TestDataset.BARCHART_2Y1G, - { - xAccessor: 'x', - yAccessors: ['y1', 'y2'], - splitSeriesAccessors: ['g'], - }, - 'spec1', - ); + const splittedSeries = splitSeries({ + id: 'spec1', + data: TestDataset.BARCHART_2Y1G, + xAccessor: 'x', + yAccessors: ['y1', 'y2'], + splitSeriesAccessors: ['g'], + }); expect(splittedSeries.rawDataSeries).toMatchSnapshot(); }); test('Can split dataset into 2Y2G series', () => { - const splittedSeries = splitSeries( - TestDataset.BARCHART_2Y2G, - { - xAccessor: 'x', - yAccessors: ['y1', 'y2'], - splitSeriesAccessors: ['g1', 'g2'], - }, - 'spec1', - ); + const splittedSeries = splitSeries({ + id: 'spec1', + data: TestDataset.BARCHART_2Y2G, + xAccessor: 'x', + yAccessors: ['y1', 'y2'], + splitSeriesAccessors: ['g1', 'g2'], + }); expect(splittedSeries.rawDataSeries).toMatchSnapshot(); }); test('Can stack simple dataseries', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 21 }, { x: 3, y1: 23 }], }, ]; @@ -108,26 +102,34 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, ]; @@ -139,14 +141,18 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 4, y1: 4 }, { x: 2, y1: 2 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 3, y1: 23 }, { x: 1, y1: 21 }], }, ]; @@ -159,14 +165,18 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: new Array(maxArrayItems).fill(0).map((d, i) => ({ x: i, y1: i })), }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: new Array(maxArrayItems).fill(0).map((d, i) => ({ x: i, y1: i })), }, ]; @@ -178,14 +188,18 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 21 }, { x: 3, y1: 23 }], }, ]; @@ -199,26 +213,34 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 3, y1: 3 }, { x: 4, y1: 4 }], }, ]; @@ -232,14 +254,18 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 3, y0: 1 }, { x: 2, y1: 3, y0: 2 }, { x: 4, y1: 4, y0: 3 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 2, y0: 1 }, { x: 2, y1: 3, y0: 1 }, { x: 3, y1: 23, y0: 4 }, { x: 4, y1: 4, y0: 1 }], }, ]; @@ -264,14 +290,18 @@ describe('Series', () => { const dataSeries: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 3, y0: 1 }, { x: 2, y1: 3, y0: 2 }, { x: 4, y1: 4, y0: 3 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 2, y0: 1 }, { x: 2, y1: 3, y0: 1 }, { x: 3, y1: 23, y0: 4 }, { x: 4, y1: 4, y0: 1 }], }, ]; @@ -386,9 +416,14 @@ describe('Series', () => { const specs = new Map(); specs.set(spec1.id, spec1); - const dataSeriesValuesA: DataSeriesColorsValues = { - specId: 'spec1', - colorValues: ['a', 'b', 'c'], + const dataSeriesValuesA: SeriesCollectionValue = { + seriesIdentifier: { + specId: 'spec1', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a', 'b', 'c'], + key: '', + }, }; const chartColors: ColorConfig = { @@ -401,7 +436,7 @@ describe('Series', () => { const emptyCustomColors = new Map(); - const defaultColorMap = getSeriesColorMap(seriesColors, chartColors, emptyCustomColors); + const defaultColorMap = getSeriesColors(seriesColors, chartColors, emptyCustomColors); const expectedDefaultColorMap = new Map(); expectedDefaultColorMap.set('spec1', 'elastic_charts_c1'); expect(defaultColorMap).toEqual(expectedDefaultColorMap); @@ -409,7 +444,7 @@ describe('Series', () => { const customColors: Map = new Map(); customColors.set('spec1', 'custom_color'); - const customizedColorMap = getSeriesColorMap(seriesColors, chartColors, customColors); + const customizedColorMap = getSeriesColors(seriesColors, chartColors, customColors); const expectedCustomizedColorMap = new Map(); expectedCustomizedColorMap.set('spec1', 'custom_color'); expect(customizedColorMap).toEqual(expectedCustomizedColorMap); @@ -439,10 +474,13 @@ describe('Series', () => { const emptyDeselected = getSplittedSeries([splitSpec]); expect(emptyDeselected.splittedSeries.get(specId)!.length).toBe(2); - const deselectedDataSeries: DataSeriesColorsValues[] = [ + const deselectedDataSeries: SeriesIdentifier[] = [ { specId, - colorValues: ['y1'], + yAccessor: splitSpec.yAccessors[0], + splitAccessors: new Map(), + seriesKeys: [], + key: 'spec{splitSpec}yAccessor{y1}splitAccessors{}', }, ]; const subsetSplit = getSplittedSeries([splitSpec], deselectedDataSeries); @@ -454,35 +492,50 @@ describe('Series', () => { const spec2Id = 'spec2'; const spec3Id = 'spec3'; - const colorValuesMap = new Map(); - const dataSeriesValues1: DataSeriesColorsValues = { - specId: spec1Id, - colorValues: [], + const seriesCollection = new Map(); + const dataSeriesValues1: SeriesCollectionValue = { + seriesIdentifier: { + specId: spec1Id, + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', + }, specSortIndex: 0, }; - const dataSeriesValues2: DataSeriesColorsValues = { - specId: spec2Id, - colorValues: [], + const dataSeriesValues2: SeriesCollectionValue = { + seriesIdentifier: { + specId: spec2Id, + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', + }, specSortIndex: 1, }; - const dataSeriesValues3: DataSeriesColorsValues = { - specId: spec3Id, - colorValues: [], + const dataSeriesValues3: SeriesCollectionValue = { + seriesIdentifier: { + specId: spec3Id, + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: '', + }, specSortIndex: 3, }; - colorValuesMap.set(spec3Id, dataSeriesValues3); - colorValuesMap.set(spec1Id, dataSeriesValues1); - colorValuesMap.set(spec2Id, dataSeriesValues2); + seriesCollection.set(spec3Id, dataSeriesValues3); + seriesCollection.set(spec1Id, dataSeriesValues1); + seriesCollection.set(spec2Id, dataSeriesValues2); const descSortedColorValues = new Map(); descSortedColorValues.set(spec1Id, dataSeriesValues1); descSortedColorValues.set(spec2Id, dataSeriesValues2); descSortedColorValues.set(spec3Id, dataSeriesValues3); - expect(getSortedDataSeriesColorsValuesMap(colorValuesMap)).toEqual(descSortedColorValues); + expect(getSortedDataSeriesColorsValuesMap(seriesCollection)).toEqual(descSortedColorValues); const ascSortedColorValues = new Map(); dataSeriesValues1.specSortIndex = 2; @@ -493,7 +546,7 @@ describe('Series', () => { ascSortedColorValues.set(spec2Id, dataSeriesValues2); ascSortedColorValues.set(spec1Id, dataSeriesValues1); - expect(getSortedDataSeriesColorsValuesMap(colorValuesMap)).toEqual(ascSortedColorValues); + expect(getSortedDataSeriesColorsValuesMap(seriesCollection)).toEqual(ascSortedColorValues); // Any series with undefined sort order should come last const undefinedSortedColorValues = new Map(); @@ -505,6 +558,6 @@ describe('Series', () => { undefinedSortedColorValues.set(spec1Id, dataSeriesValues1); undefinedSortedColorValues.set(spec2Id, dataSeriesValues2); - expect(getSortedDataSeriesColorsValuesMap(colorValuesMap)).toEqual(undefinedSortedColorValues); + expect(getSortedDataSeriesColorsValuesMap(seriesCollection)).toEqual(undefinedSortedColorValues); }); }); diff --git a/src/chart_types/xy_chart/utils/series.ts b/src/chart_types/xy_chart/utils/series.ts index 379ac72883..b4e562d5d0 100644 --- a/src/chart_types/xy_chart/utils/series.ts +++ b/src/chart_types/xy_chart/utils/series.ts @@ -3,8 +3,7 @@ import { Accessor } from '../../../utils/accessor'; import { GroupId, SpecId } from '../../../utils/ids'; import { splitSpecsByGroupId, YBasicSeriesSpec } from '../domains/y_domain'; import { formatNonStackedDataSeriesValues } from './nonstacked_series_utils'; -import { isEqualSeriesKey } from './series_utils'; -import { BasicSeriesSpec, Datum, SeriesAccessors, SeriesSpecs, SeriesTypes } from './specs'; +import { BasicSeriesSpec, Datum, SubSeriesStringPredicate, SeriesTypes, SeriesSpecs } from './specs'; import { formatStackedDataSeriesValues } from './stacked_series_utils'; import { ScaleType } from '../../../utils/scales/scales'; import { LastValues } from '../state/utils'; @@ -46,18 +45,23 @@ export interface DataSeriesDatum { filled?: FilledValues; } -export interface DataSeries { +export interface SeriesIdentifier { specId: SpecId; - key: any[]; - seriesColorKey: string; - data: DataSeriesDatum[]; + yAccessor: string | number; + splitAccessors: Map; // does the map have a size vs making it optional + seriesKeys: (string | number)[]; + key: string; } -export interface RawDataSeries { - specId: SpecId; - key: any[]; - seriesColorKey: string; + +export type DataSeries = SeriesIdentifier & { + // seriesColorKey: string; + data: DataSeriesDatum[]; +}; + +export type RawDataSeries = SeriesIdentifier & { + // seriesColorKey: string; data: RawDataSeriesDatum[]; -} +}; export interface FormattedDataSeries { groupId: GroupId; @@ -71,25 +75,19 @@ export interface DataSeriesCounts { areaSeries: number; } -export interface DataSeriesColorsValues { - specId: SpecId; +export type SeriesCollectionValue = { banded?: boolean; - colorValues: any[]; lastValue?: LastValues; specSortIndex?: number; -} + seriesIdentifier: SeriesIdentifier; +}; -export function findDataSeriesByColorValues( - series: DataSeriesColorsValues[] | null, - value: DataSeriesColorsValues, -): number { +export function getSeriesIndex(series: SeriesIdentifier[], target: SeriesIdentifier): number { if (!series) { return -1; } - return series.findIndex((item: DataSeriesColorsValues) => { - return isEqualSeriesKey(item.colorValues, value.colorValues) && item.specId === value.specId; - }); + return series.findIndex(({ key }) => target.key === key); } /** @@ -97,44 +95,41 @@ export function findDataSeriesByColorValues( * Each series is then associated with a key thats belong to its configuration. * */ -export function splitSeries( - data: Datum[], - accessors: SeriesAccessors, - specId: SpecId, -): { +export function splitSeries({ + id: specId, + data, + xAccessor, + yAccessors, + y0Accessors, + splitSeriesAccessors = [], +}: Pick): { rawDataSeries: RawDataSeries[]; - colorsValues: Map; + colorsValues: Set; xValues: Set; } { - const { xAccessor, yAccessors, y0Accessors, splitSeriesAccessors = [] } = accessors; const isMultipleY = yAccessors && yAccessors.length > 1; const series = new Map(); - const colorsValues = new Map(); + const colorsValues = new Set(); const xValues = new Set(); data.forEach((datum) => { - const seriesKey = getAccessorsValues(datum, splitSeriesAccessors); + const splitAccessors = getSplitAccessors(datum, splitSeriesAccessors); if (isMultipleY) { yAccessors.forEach((accessor, index) => { - const colorValues = getColorValues(datum, splitSeriesAccessors, accessor); - const colorValuesKey = getColorValuesAsString(colorValues, specId); - colorsValues.set(colorValuesKey, colorValues); const cleanedDatum = cleanDatum(datum, xAccessor, accessor, y0Accessors && y0Accessors[index]); if (cleanedDatum.x !== null && cleanedDatum.x !== undefined) { xValues.add(cleanedDatum.x); - updateSeriesMap(series, [...seriesKey, accessor], cleanedDatum, specId, colorValuesKey); + const seriesKey = updateSeriesMap(series, splitAccessors, accessor, cleanedDatum, specId); + colorsValues.add(seriesKey); } }); } else { - const colorValues = getColorValues(datum, splitSeriesAccessors); - const colorValuesKey = getColorValuesAsString(colorValues, specId); - colorsValues.set(colorValuesKey, colorValues); const cleanedDatum = cleanDatum(datum, xAccessor, yAccessors[0], y0Accessors && y0Accessors[0]); - if (cleanedDatum.x !== null && cleanedDatum.x !== undefined) { xValues.add(cleanedDatum.x); - updateSeriesMap(series, [...seriesKey], cleanedDatum, specId, colorValuesKey); + const seriesKey = updateSeriesMap(series, splitAccessors, yAccessors[0], cleanedDatum, specId); + colorsValues.add(seriesKey); } } }); @@ -146,58 +141,66 @@ export function splitSeries( }; } +/** + * Gets global series key to id any series as a string + */ +export function getSeriesKey({ + specId, + yAccessor, + splitAccessors, +}: Pick): string { + const joinedAccessors = [...splitAccessors.entries()] + .sort(([a], [b]) => (a > b ? 1 : -1)) + .map(([key, value]) => `${key}-${value}`) + .join('|'); + return `spec{${specId}}yAccessor{${yAccessor}}splitAccessors{${joinedAccessors}}`; +} + /** * Mutate the passed map adding or updating the DataSeries stored * along with the series key */ function updateSeriesMap( seriesMap: Map, - seriesKey: any[], + splitAccessors: Map, + accessor: any, datum: RawDataSeriesDatum, specId: SpecId, - seriesColorKey: string, -): Map { - const seriesKeyString = seriesKey.join('___'); - const series = seriesMap.get(seriesKeyString); +): string { + const seriesKeys = [...splitAccessors.values(), accessor]; + const seriesKey = getSeriesKey({ + specId, + yAccessor: accessor, + splitAccessors, + }); + const series = seriesMap.get(seriesKey); if (series) { series.data.push(datum); } else { - seriesMap.set(seriesKeyString, { + seriesMap.set(seriesKey, { specId, - seriesColorKey, - key: seriesKey, + yAccessor: accessor, + splitAccessors, data: [datum], + key: seriesKey, + seriesKeys, }); } - return seriesMap; + return seriesKey; } /** * Get the array of values that forms a series key */ -function getAccessorsValues(datum: Datum, accessors: Accessor[] = []): any[] { - return accessors - .map((accessor) => { - return datum[accessor]; - }) - .filter((value) => value !== undefined); -} - -/** - * Get the array of values that forms a series key - */ -function getColorValues(datum: Datum, accessors: Accessor[] = [], yAccessorValue?: any): any[] { - const colorValues = getAccessorsValues(datum, accessors); - if (yAccessorValue) { - return [...colorValues, yAccessorValue]; - } - return colorValues; -} -/** - * Get the array of values that forms a series key - */ -export function getColorValuesAsString(colorValues: any[], specId: SpecId): string { - return `specId:{${specId}},colors:{${colorValues}}`; +function getSplitAccessors(datum: Datum, accessors: Accessor[] = []): Map { + const splitAccessors = new Map(); + accessors.forEach((accessor) => { + const value = datum[accessor]; + if (value !== undefined || value !== null) { + splitAccessors.set(accessor, value); + } + }); + return splitAccessors; } /** @@ -315,45 +318,37 @@ function getRawDataSeries( */ export function getSplittedSeries( seriesSpecs: BasicSeriesSpec[], - deselectedDataSeries: DataSeriesColorsValues[] = [], + deselectedDataSeries: SeriesIdentifier[] = [], ): { splittedSeries: Map; - seriesColors: Map; + seriesCollection: Map; xValues: Set; } { const splittedSeries = new Map(); - const seriesColors = new Map(); - let xValues: Set = new Set(); + const seriesCollection = new Map(); + const xValues: Set = new Set(); let isOrdinalScale = false; for (const spec of seriesSpecs) { + const dataSeries = splitSeries(spec); + let currentRawDataSeries = dataSeries.rawDataSeries; if (spec.xScaleType === ScaleType.Ordinal) { isOrdinalScale = true; } - const dataSeries = splitSeries(spec.data, spec, spec.id); - let currentRawDataSeries = dataSeries.rawDataSeries; if (deselectedDataSeries.length > 0) { - currentRawDataSeries = dataSeries.rawDataSeries.filter( - (series): boolean => { - const seriesValues = { - specId: spec.id, - colorValues: series.key, - }; - - return findDataSeriesByColorValues(deselectedDataSeries, seriesValues) < 0; - }, - ); + currentRawDataSeries = dataSeries.rawDataSeries.filter(({ key }) => { + return !deselectedDataSeries.some(({ key: deselectedKey }) => key === deselectedKey); + }); } splittedSeries.set(spec.id, currentRawDataSeries); const banded = spec.y0Accessors && spec.y0Accessors.length > 0; - dataSeries.colorsValues.forEach((colorValues, key) => { - seriesColors.set(key, { - specId: spec.id, - specSortIndex: spec.sortIndex, + dataSeries.rawDataSeries.forEach((series) => { + seriesCollection.set(series.key, { banded, - colorValues, + specSortIndex: spec.sortIndex, + seriesIdentifier: series as SeriesIdentifier, }); }); @@ -361,45 +356,124 @@ export function getSplittedSeries( xValues.add(xValue); } } - // keep the user order for ordinal scales - if (!isOrdinalScale) { - xValues = new Set([...xValues].sort()); - } + return { splittedSeries, - seriesColors, - xValues, + seriesCollection, + // keep the user order for ordinal scales + xValues: isOrdinalScale ? xValues : new Set([...xValues].sort()), }; } -function getSortIndex({ specSortIndex }: DataSeriesColorsValues, total: number): number { +/** + * Get custom series sub-name + */ +const getCustomSubSeriesName = (() => { + const cache = new Map(); + + return (customSubSeriesLabel: SubSeriesStringPredicate, isTooltip: boolean) => ( + args: [string | number | null, string | number], + ): string | number => { + const [accessorKey, accessorLabel] = args; + const key = [args, isTooltip].join('~~~'); + + if (cache.has(key)) { + return cache.get(key); + } else { + const label = customSubSeriesLabel(accessorLabel, accessorKey, isTooltip) || accessorLabel; + cache.set(key, label); + + return label; + } + }; +})(); + +const getSeriesLabelKeys = ( + spec: BasicSeriesSpec, + seriesIdentifier: SeriesIdentifier, + isTooltip: boolean, +): (string | number)[] => { + const isMultipleY = spec.yAccessors.length > 1; + + if (spec.customSubSeriesLabel) { + const { yAccessor, splitAccessors } = seriesIdentifier; + const fullKeyPairs: [string | number | null, string | number][] = [...splitAccessors.entries(), [null, yAccessor]]; + const labelKeys = fullKeyPairs.map(getCustomSubSeriesName(spec.customSubSeriesLabel, isTooltip)); + + return isMultipleY ? labelKeys : labelKeys.slice(0, -1); + } + + const { seriesKeys } = seriesIdentifier; + + return isMultipleY ? seriesKeys : seriesKeys.slice(0, -1); +}; + +/** + * Get series label based on `SeriesIdentifier` + */ +export function getSeriesLabel( + seriesIdentifier: SeriesIdentifier, + hasSingleSeries: boolean, + isTooltip: boolean, + spec?: BasicSeriesSpec, +): string { + if (spec && spec.customSeriesLabel) { + const customLabel = spec.customSeriesLabel(seriesIdentifier, isTooltip); + + if (customLabel !== null) { + return customLabel; + } + } + + let label = ''; + const labelKeys = spec ? getSeriesLabelKeys(spec, seriesIdentifier, isTooltip) : seriesIdentifier.seriesKeys; + + // there is one series, the is only one yAccessor, the first part is not null + if (hasSingleSeries || labelKeys.length === 0 || labelKeys[0] == null) { + if (!spec) { + return ''; + } + + if (spec.splitSeriesAccessors && labelKeys.length > 0 && labelKeys[0] != null) { + label = labelKeys.join(' - '); + } else { + label = spec.name || `${spec.id}`; + } + } else { + label = labelKeys.join(' - '); + } + + return label; +} + +function getSortIndex({ specSortIndex }: SeriesCollectionValue, total: number): number { return specSortIndex != null ? specSortIndex : total; } export function getSortedDataSeriesColorsValuesMap( - colorValuesMap: Map, -): Map { - const seriesColorsArray = [...colorValuesMap]; + seriesCollection: Map, +): Map { + const seriesColorsArray = [...seriesCollection]; seriesColorsArray.sort(([, specA], [, specB]) => { - return getSortIndex(specA, colorValuesMap.size) - getSortIndex(specB, colorValuesMap.size); + return getSortIndex(specA, seriesCollection.size) - getSortIndex(specB, seriesCollection.size); }); return new Map([...seriesColorsArray]); } -export function getSeriesColorMap( - seriesColors: Map, +export function getSeriesColors( + seriesCollection: Map, chartColors: ColorConfig, customColors: Map, ): Map { const seriesColorMap = new Map(); let counter = 0; - seriesColors.forEach((_, key) => { - const customSeriesColor: string | undefined = customColors.get(key); + seriesCollection.forEach((_, seriesKey) => { + const customSeriesColor: string | undefined = customColors.get(seriesKey); const color = customSeriesColor || chartColors.vizColors[counter % chartColors.vizColors.length]; - seriesColorMap.set(key, color); + seriesColorMap.set(seriesKey, color); counter++; }); return seriesColorMap; diff --git a/src/chart_types/xy_chart/utils/series_utils.test.ts b/src/chart_types/xy_chart/utils/series_utils.test.ts deleted file mode 100644 index 6c17e3f11a..0000000000 --- a/src/chart_types/xy_chart/utils/series_utils.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { DataSeriesColorsValues } from './series'; -import { belongsToDataSeries, isEqualSeriesKey } from './series_utils'; -import { GeometryId } from '../../../utils/geometry'; - -describe('Series utility functions', () => { - test('can compare series keys for identity', () => { - const seriesKeyA = ['a', 'b', 'c']; - const seriesKeyB = ['a', 'b', 'c']; - const seriesKeyC = ['a', 'b', 'd']; - const seriesKeyD = ['d']; - const seriesKeyE = ['b', 'a', 'c']; - - expect(isEqualSeriesKey(seriesKeyA, seriesKeyB)).toBe(true); - expect(isEqualSeriesKey(seriesKeyB, seriesKeyC)).toBe(false); - expect(isEqualSeriesKey(seriesKeyA, seriesKeyD)).toBe(false); - expect(isEqualSeriesKey(seriesKeyA, seriesKeyE)).toBe(false); - expect(isEqualSeriesKey(seriesKeyA, [])).toBe(false); - }); - - test('can determine if a geometry id belongs to a data series', () => { - const geometryIdA: GeometryId = { - specId: 'a', - seriesKey: ['a', 'b', 'c'], - }; - - const dataSeriesValuesA: DataSeriesColorsValues = { - specId: 'a', - colorValues: ['a', 'b', 'c'], - }; - - const dataSeriesValuesB: DataSeriesColorsValues = { - specId: 'b', - colorValues: ['a', 'b', 'c'], - }; - - const dataSeriesValuesC: DataSeriesColorsValues = { - specId: 'a', - colorValues: ['a', 'b', 'd'], - }; - - expect(belongsToDataSeries(geometryIdA, dataSeriesValuesA)).toBe(true); - expect(belongsToDataSeries(geometryIdA, dataSeriesValuesB)).toBe(false); - expect(belongsToDataSeries(geometryIdA, dataSeriesValuesC)).toBe(false); - }); -}); diff --git a/src/chart_types/xy_chart/utils/series_utils.ts b/src/chart_types/xy_chart/utils/series_utils.ts deleted file mode 100644 index aa77102942..0000000000 --- a/src/chart_types/xy_chart/utils/series_utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DataSeriesColorsValues } from './series'; -import { GeometryId } from '../../../utils/geometry'; - -export function isEqualSeriesKey(a: any[], b: any[]): boolean { - if (a.length !== b.length) { - return false; - } - - for (let i = 0, l = a.length; i < l; i++) { - if (a[i] !== b[i]) { - return false; - } - } - - return true; -} - -export function belongsToDataSeries(geometryValue: GeometryId, dataSeriesValues: DataSeriesColorsValues): boolean { - const legendItemSeriesKey = dataSeriesValues.colorValues; - const legendItemSpecId = dataSeriesValues.specId; - - const geometrySeriesKey = geometryValue.seriesKey; - const geometrySpecId = geometryValue.specId; - - const hasSameSpecId = legendItemSpecId === geometrySpecId; - const hasSameSeriesKey = isEqualSeriesKey(legendItemSeriesKey, geometrySeriesKey); - - return hasSameSpecId && hasSameSeriesKey; -} diff --git a/src/chart_types/xy_chart/utils/specs.ts b/src/chart_types/xy_chart/utils/specs.ts index a7c73e494c..f0b518fca1 100644 --- a/src/chart_types/xy_chart/utils/specs.ts +++ b/src/chart_types/xy_chart/utils/specs.ts @@ -13,9 +13,8 @@ import { RecursivePartial } from '../../../utils/commons'; import { AxisId, GroupId } from '../../../utils/ids'; import { ScaleContinuousType, ScaleType } from '../../../utils/scales/scales'; import { CurveType } from '../../../utils/curves'; -import { RawDataSeriesDatum, DataSeriesColorsValues } from './series'; +import { RawDataSeriesDatum, SeriesIdentifier } from './series'; import { AnnotationTooltipFormatter } from '../annotations/annotation_utils'; -import { GeometryId } from '../../../utils/geometry'; import { Spec } from '../../..'; import { ChartTypes } from '../..'; @@ -51,7 +50,7 @@ export type SpecTypes = $Values; * - `RecursivePartial`: Style values to be merged with base bar styles * - `null`: Keep existing bar style */ -export type BarStyleAccessor = (datum: RawDataSeriesDatum, geometryId: GeometryId) => BarStyleOverride; +export type BarStyleAccessor = (datum: RawDataSeriesDatum, seriesIdentifier: SeriesIdentifier) => BarStyleOverride; /** * Override for bar styles per datum * @@ -60,9 +59,17 @@ export type BarStyleAccessor = (datum: RawDataSeriesDatum, geometryId: GeometryI * - `RecursivePartial`: Style values to be merged with base point styles * - `null`: Keep existing point style */ -export type PointStyleAccessor = (datum: RawDataSeriesDatum, geometryId: GeometryId) => PointStyleOverride; +export type PointStyleAccessor = (datum: RawDataSeriesDatum, seriesIdentifier: SeriesIdentifier) => PointStyleOverride; export const DEFAULT_GLOBAL_ID = '__global__'; +export type FilterPredicate = (series: SeriesIdentifier) => boolean; +export type SeriesStringPredicate = (series: SeriesIdentifier, isTooltip: boolean) => string | null; +export type SubSeriesStringPredicate = ( + accessorLabel: string | number, + accessorKey: string | number | null, + isTooltip: boolean, +) => string | number | null; + interface DomainMinInterval { /** Custom minInterval for the domain which will affect data bucket size. * The minInterval cannot be greater than the computed minimum interval between any two adjacent data points. @@ -204,8 +211,8 @@ export interface SeriesSpec extends Spec { data: Datum[]; /** The type of series you are looking to render */ seriesType: SeriesTypes; - /** Custom colors for series */ - customSeriesColors?: CustomSeriesColorsMap; + /** Set colors for specific series */ + customSeriesColors?: CustomSeriesColors; /** If the series should appear in the legend * @default false */ @@ -225,6 +232,25 @@ export interface SeriesSpec extends Spec { * @default ' - lower' */ y1AccessorFormat?: AccessorFormat; + /** + * Hide series in tooltip + */ + filterSeriesInTooltip?: FilterPredicate; + /** + * Custom series naming predicate function. Values are unaffected by `customSubSeriesLabel` changes. + * + * This takes precedence over `customSubSeriesLabel` + * + * @param series - `SeriesIdentifier` + * @param isTooltip - true if tooltip label, otherwise legend label + */ + customSeriesLabel?: SeriesStringPredicate; + /** + * Custom sub series naming predicate function. + * + * `customSeriesLabel` takes precedence + */ + customSubSeriesLabel?: SubSeriesStringPredicate; } export interface Postfixes { @@ -242,7 +268,9 @@ export interface Postfixes { y1AccessorFormat?: string; } -export type CustomSeriesColorsMap = Map; +export type SeriesColorsArray = string[]; +export type SeriesColorAccessorFn = (seriesIdentifier: SeriesIdentifier) => string | null; +export type CustomSeriesColors = SeriesColorsArray | SeriesColorAccessorFn; export interface SeriesAccessors { /** The field name of the x value on Datum object */ diff --git a/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts b/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts index 0e1c6559ba..fa9b89f1c7 100644 --- a/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts +++ b/src/chart_types/xy_chart/utils/stacked_percent_series_utils.test.ts @@ -11,8 +11,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -22,8 +24,10 @@ describe('Stacked Series Utils', () => { y1: 20, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -33,8 +37,10 @@ describe('Stacked Series Utils', () => { y1: 70, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; @@ -46,8 +52,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -57,8 +65,10 @@ describe('Stacked Series Utils', () => { y1: null, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -68,8 +78,10 @@ describe('Stacked Series Utils', () => { y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; @@ -82,8 +94,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -94,8 +108,10 @@ describe('Stacked Series Utils', () => { y1: 20, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -106,8 +122,10 @@ describe('Stacked Series Utils', () => { y1: 70, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; @@ -120,8 +138,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -131,8 +151,10 @@ describe('Stacked Series Utils', () => { y1: null, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -143,22 +165,28 @@ describe('Stacked Series Utils', () => { y1: 90, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; const DATA_SET_WITH_NULL_2: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 10 }, { x: 2, y1: 20 }, { x: 4, y1: 40 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 90 }, { x: 3, y1: 30 }], }, ]; diff --git a/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts b/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts index fccc54907c..a9be8d2007 100644 --- a/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts +++ b/src/chart_types/xy_chart/utils/stacked_series_utils.test.ts @@ -6,8 +6,10 @@ describe('Stacked Series Utils', () => { const EMPTY_DATA_SET: RawDataSeries[] = [ { data: [], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, ]; @@ -19,8 +21,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -30,8 +34,10 @@ describe('Stacked Series Utils', () => { y1: 20, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -41,8 +47,10 @@ describe('Stacked Series Utils', () => { y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; @@ -54,8 +62,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -65,8 +75,10 @@ describe('Stacked Series Utils', () => { y1: null, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -76,8 +88,10 @@ describe('Stacked Series Utils', () => { y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; @@ -90,8 +104,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -102,8 +118,10 @@ describe('Stacked Series Utils', () => { y1: 20, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -114,8 +132,10 @@ describe('Stacked Series Utils', () => { y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; @@ -128,8 +148,10 @@ describe('Stacked Series Utils', () => { y1: 10, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec1', }, { @@ -139,8 +161,10 @@ describe('Stacked Series Utils', () => { y1: null, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec2', }, { @@ -151,22 +175,28 @@ describe('Stacked Series Utils', () => { y1: 30, }, ], - key: [], - seriesColorKey: 'color-key', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: [], + key: 'color-key', specId: 'spec3', }, ]; const DATA_SET_WITH_NULL_2: RawDataSeries[] = [ { specId: 'spec1', - key: ['a'], - seriesColorKey: 'a', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['a'], + key: 'a', data: [{ x: 1, y1: 1 }, { x: 2, y1: 2 }, { x: 4, y1: 4 }], }, { specId: 'spec1', - key: ['b'], - seriesColorKey: 'b', + yAccessor: 'y1', + splitAccessors: new Map(), + seriesKeys: ['b'], + key: 'b', data: [{ x: 1, y1: 21 }, { x: 3, y1: 23 }], }, ]; diff --git a/src/chart_types/xy_chart/utils/stacked_series_utils.ts b/src/chart_types/xy_chart/utils/stacked_series_utils.ts index 53d5b5bb33..0a3f02f125 100644 --- a/src/chart_types/xy_chart/utils/stacked_series_utils.ts +++ b/src/chart_types/xy_chart/utils/stacked_series_utils.ts @@ -143,9 +143,7 @@ export function formatStackedDataSeriesValues( } newData.sort(datumXSortPredicate(xScaleType)); return { - specId: ds.specId, - key: ds.key, - seriesColorKey: ds.seriesColorKey, + ...ds, data: newData, }; }); diff --git a/src/components/_index.scss b/src/components/_index.scss index 74f4c59335..f0a6ab8c6f 100644 --- a/src/components/_index.scss +++ b/src/components/_index.scss @@ -6,4 +6,4 @@ @import 'legend/index'; @import 'unavailable_chart'; -@import '../chart_types/xy_chart/renderer/index' \ No newline at end of file +@import '../chart_types/xy_chart/renderer/index'; diff --git a/src/components/legend/legend.tsx b/src/components/legend/legend.tsx index 015aa46691..9c8caff0e2 100644 --- a/src/components/legend/legend.tsx +++ b/src/components/legend/legend.tsx @@ -15,7 +15,6 @@ import { LIGHT_THEME } from '../../utils/themes/light_theme'; import { LegendListItem } from './legend_item'; import { Theme } from '../../utils/themes/theme'; import { TooltipLegendValue } from '../../chart_types/xy_chart/tooltip/tooltip'; -import { AccessorType } from '../../utils/geometry'; import { LegendItem, getItemLabel } from '../../chart_types/xy_chart/legend/legend'; import { BBox } from '../../utils/bbox/bbox_calculator'; import { @@ -24,6 +23,7 @@ import { onLegendItemOverAction, } from '../../state/actions/legend'; import { SettingsSpec } from '../../specs'; +import { BandedAccessorType } from '../../utils/geometry'; interface LegendStateProps { legendItems: Map; @@ -142,7 +142,7 @@ class LegendComponent extends React.Component { const { showLegendDisplayValue, legendPosition } = settings; const legendValues = this.getLegendValues(legendItemTooltipValues, key, banded); return legendValues.map((value, index) => { - const yAccessor: AccessorType = index === 0 ? AccessorType.Y1 : AccessorType.Y0; + const yAccessor: BandedAccessorType = index === 0 ? BandedAccessorType.Y1 : BandedAccessorType.Y0; return ( void; + toggleDeselectSeriesAction: (legendItemId: SeriesIdentifier) => void; } interface LegendItemState { @@ -98,15 +97,12 @@ export class LegendListItem extends React.PureComponent () => { + onVisibilityClick = (legendItemId: SeriesIdentifier) => () => { const { onLegendItemClickListener, toggleDeselectSeriesAction } = this.props; if (onLegendItemClickListener) { onLegendItemClickListener(legendItemId); diff --git a/src/index.ts b/src/index.ts index e231d32cda..f712eb3953 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,14 +11,15 @@ export { RecursivePartial } from './utils/commons'; export { CurveType } from './utils/curves'; export { timeFormatter, niceTimeFormatter, niceTimeFormatByDay } from './utils/data/formatters'; export { DataGenerator } from './utils/data_generators/data_generator'; +export { SeriesCollectionValue } from './chart_types/xy_chart/utils/series'; export { ChartTypes } from './chart_types'; export { Position, Rendering, Rotation, TickFormatter } from './chart_types/xy_chart/utils/specs'; export { TooltipType, TooltipValue, TooltipValueFormatter } from './chart_types/xy_chart/utils/interactions'; -export { DataSeriesColorsValues } from './chart_types/xy_chart/utils/series'; +export { SeriesIdentifier } from './chart_types/xy_chart/utils/series'; export { AnnotationDomainType, AnnotationDomainTypes, - CustomSeriesColorsMap, + CustomSeriesColors, HistogramModeAlignment, HistogramModeAlignments, LineAnnotationDatum, diff --git a/src/mocks/series/series.ts b/src/mocks/series/series.ts index d8ba605dc8..b5a1ab39d2 100644 --- a/src/mocks/series/series.ts +++ b/src/mocks/series/series.ts @@ -14,8 +14,10 @@ import { FullDataSeriesDatum, WithIndex } from '../../chart_types/xy_chart/utils export class MockDataSeries { private static readonly base: DataSeries = { specId: getSpecId('spec1'), - key: ['spec1'], - seriesColorKey: 'spec1', + seriesKeys: ['spec1'], + yAccessor: 'y', + splitAccessors: new Map(), + key: 'spec1', data: [], }; @@ -48,8 +50,10 @@ export class MockDataSeries { export class MockRawDataSeries { private static readonly base: RawDataSeries = { specId: getSpecId('spec1'), - key: ['spec1'], - seriesColorKey: 'spec1', + seriesKeys: ['spec1'], + yAccessor: 'y', + splitAccessors: new Map(), + key: 'spec1', data: [], }; diff --git a/src/mocks/series/seriesIdentifiers.ts b/src/mocks/series/seriesIdentifiers.ts new file mode 100644 index 0000000000..38f3b07671 --- /dev/null +++ b/src/mocks/series/seriesIdentifiers.ts @@ -0,0 +1,32 @@ +import { BasicSeriesSpec } from '../../chart_types/xy_chart/utils/specs'; +import { getSpecId } from '../..'; +import { SeriesCollectionValue, getSplittedSeries, SeriesIdentifier } from '../../chart_types/xy_chart/utils/series'; +import { mergePartial } from '../../utils/commons'; + +type SeriesCollection = Map; + +export class MockSeriesCollection { + static empty(): SeriesCollection { + return new Map(); + } + + static fromSpecs(seriesSpecs: BasicSeriesSpec[]) { + const { seriesCollection } = getSplittedSeries(seriesSpecs, []); + + return seriesCollection; + } +} + +export class MockSeriesIdentifier { + private static readonly base: SeriesIdentifier = { + specId: getSpecId('bars'), + yAccessor: 'y', + seriesKeys: ['a'], + splitAccessors: new Map().set('g', 'a'), + key: 'spec{bars}yAccessor{y}splitAccessors{g-a}', + }; + + static default(partial?: Partial) { + return mergePartial(MockSeriesIdentifier.base, partial, { mergeOptionalPartialValues: true }); + } +} diff --git a/src/mocks/specs/specs.ts b/src/mocks/specs/specs.ts index 0a831adfec..7c76e445f9 100644 --- a/src/mocks/specs/specs.ts +++ b/src/mocks/specs/specs.ts @@ -26,6 +26,7 @@ export class MockSeriesSpec { yScaleType: ScaleType.Linear, xAccessor: 'x', yAccessors: ['y'], + splitSeriesAccessors: ['g'], yScaleToDataExtent: false, hideInLegend: false, enableHistogramMode: false, @@ -98,6 +99,22 @@ export class MockSeriesSpec { static line(partial?: Partial): LineSeriesSpec { return mergePartial(MockSeriesSpec.lineBase, partial, { mergeOptionalPartialValues: true }); } + + static byType(type?: 'line' | 'bar' | 'area'): BasicSeriesSpec { + switch (type) { + case 'line': + return MockSeriesSpec.lineBase; + break; + case 'bar': + return MockSeriesSpec.barBase; + break; + case 'area': + return MockSeriesSpec.areaBase; + break; + default: + return MockSeriesSpec.barBase; + } + } } export class MockSeriesSpecs { @@ -105,6 +122,13 @@ export class MockSeriesSpecs { return specs; } + static fromPartialSpecs(specs: Partial[]): SeriesSpecs { + return specs.map(({ seriesType, ...spec }) => { + const base = MockSeriesSpec.byType(seriesType); + return mergePartial(base, spec, { mergeOptionalPartialValues: true }); + }); + } + static empty(): SeriesSpecs { return []; } diff --git a/src/mocks/utils.ts b/src/mocks/utils.ts index a7bd61e59a..a617a9da79 100644 --- a/src/mocks/utils.ts +++ b/src/mocks/utils.ts @@ -1,3 +1,7 @@ +import seedrandom from 'seedrandom'; + +import { DataGenerator } from '../../src'; + /** * Forces object to be partial type for mocking tests * @@ -8,3 +12,11 @@ export const forcedType = (obj: Partial): T => { return obj as T; }; + +export const getRandomNumber = seedrandom(process.env.RNG_SEED || undefined); + +export class SeededDataGenerator extends DataGenerator { + constructor(frequency = 500) { + super(frequency, getRandomNumber); + } +} diff --git a/src/specs/settings.tsx b/src/specs/settings.tsx index 1d44143991..6efff372be 100644 --- a/src/specs/settings.tsx +++ b/src/specs/settings.tsx @@ -8,12 +8,12 @@ import { Spec } from '.'; import { LIGHT_THEME } from '../utils/themes/light_theme'; import { ChartTypes } from '../chart_types'; import { GeometryValue } from '../utils/geometry'; -import { DataSeriesColorsValues } from '../chart_types/xy_chart/utils/series'; +import { SeriesIdentifier } from '../chart_types/xy_chart/utils/series'; export type ElementClickListener = (values: GeometryValue[]) => void; export type ElementOverListener = (values: GeometryValue[]) => void; export type BrushEndListener = (min: number, max: number) => void; -export type LegendItemListener = (dataSeriesIdentifiers: DataSeriesColorsValues | null) => void; +export type LegendItemListener = (series: SeriesIdentifier | null) => void; export type CursorUpdateListener = (event?: CursorEvent) => void; /** * Listener to be called when chart render state changes diff --git a/src/state/actions/legend.ts b/src/state/actions/legend.ts index 9884bda069..3defd22d79 100644 --- a/src/state/actions/legend.ts +++ b/src/state/actions/legend.ts @@ -1,4 +1,4 @@ -import { DataSeriesColorsValues } from '../../chart_types/xy_chart/utils/series'; +import { SeriesIdentifier } from '../../chart_types/xy_chart/utils/series'; export const ON_TOGGLE_LEGEND = 'ON_TOGGLE_LEGEND'; export const ON_LEGEND_ITEM_OVER = 'ON_LEGEND_ITEM_OVER'; @@ -18,7 +18,7 @@ interface LegendItemOutAction { interface ToggleDeselectSeriesAction { type: typeof ON_TOGGLE_DESELECT_SERIES; - legendItemId: DataSeriesColorsValues; + legendItemId: SeriesIdentifier; } export function onToggleLegend(): ToggleLegendAction { @@ -33,7 +33,7 @@ export function onLegendItemOutAction(): LegendItemOutAction { return { type: ON_LEGEND_ITEM_OUT }; } -export function onToggleDeselectSeriesAction(legendItemId: DataSeriesColorsValues): ToggleDeselectSeriesAction { +export function onToggleDeselectSeriesAction(legendItemId: SeriesIdentifier): ToggleDeselectSeriesAction { return { type: ON_TOGGLE_DESELECT_SERIES, legendItemId }; } diff --git a/src/state/chart_state.ts b/src/state/chart_state.ts index ec3a6ac4f1..074ff08cc9 100644 --- a/src/state/chart_state.ts +++ b/src/state/chart_state.ts @@ -2,7 +2,7 @@ import { SPEC_PARSED, SPEC_UNMOUNTED, UPSERT_SPEC, REMOVE_SPEC } from './actions import { interactionsReducer } from './reducers/interactions'; import { ChartTypes } from '../chart_types'; import { XYAxisChartState } from '../chart_types/xy_chart/state/chart_state'; -import { DataSeriesColorsValues } from '../chart_types/xy_chart/utils/series'; +import { SeriesIdentifier } from '../chart_types/xy_chart/utils/series'; import { Spec } from '../specs'; import { DEFAULT_SETTINGS_SPEC, CursorEvent } from '../specs/settings'; import { Dimensions } from '../utils/dimensions'; @@ -65,7 +65,7 @@ export interface InteractionsState { highlightedLegendItemKey: string | null; legendCollapsed: boolean; invertDeselect: boolean; - deselectedDataSeries: DataSeriesColorsValues[]; + deselectedDataSeries: SeriesIdentifier[]; } export interface ExternalEventsState { diff --git a/src/state/reducers/interactions.ts b/src/state/reducers/interactions.ts index 64b1542737..3a5a9ccfd2 100644 --- a/src/state/reducers/interactions.ts +++ b/src/state/reducers/interactions.ts @@ -7,7 +7,7 @@ import { LegendActions, } from '../actions/legend'; import { ON_MOUSE_DOWN, ON_MOUSE_UP, ON_POINTER_MOVE, MouseActions } from '../actions/mouse'; -import { DataSeriesColorsValues, findDataSeriesByColorValues } from '../../chart_types/xy_chart/utils/series'; +import { getSeriesIndex, SeriesIdentifier } from '../../chart_types/xy_chart/utils/series'; export function interactionsReducer(state: InteractionsState, action: LegendActions | MouseActions): InteractionsState { switch (action.type) { @@ -107,11 +107,8 @@ export function interactionsReducer(state: InteractionsState, action: LegendActi } } -function toggleDeselectedDataSeries( - legendItem: DataSeriesColorsValues, - deselectedDataSeries: DataSeriesColorsValues[], -) { - const index = findDataSeriesByColorValues(deselectedDataSeries, legendItem); +function toggleDeselectedDataSeries(legendItem: SeriesIdentifier, deselectedDataSeries: SeriesIdentifier[]) { + const index = getSeriesIndex(deselectedDataSeries, legendItem); if (index > -1) { return [...deselectedDataSeries.slice(0, index), ...deselectedDataSeries.slice(index + 1)]; } else { diff --git a/src/utils/data_generators/data_generator.ts b/src/utils/data_generators/data_generator.ts index a58c841303..7126f3efd7 100644 --- a/src/utils/data_generators/data_generator.ts +++ b/src/utils/data_generators/data_generator.ts @@ -7,7 +7,8 @@ export class DataGenerator { this.generator = new Simple1DNoise(randomNumberGenerator); this.frequency = frequency; } - generateSimpleSeries(totalPoints = 50, group = 1, groupPrefix = '') { + generateSimpleSeries(totalPoints = 50, groupIndex = 1, groupPrefix = '') { + const group = String.fromCharCode(97 + groupIndex); const dataPoints = new Array(totalPoints).fill(0).map((_, i) => { return { x: i, @@ -19,6 +20,7 @@ export class DataGenerator { } generateGroupedSeries(totalPoints = 50, totalGroups = 2, groupPrefix = '') { const groups = new Array(totalGroups).fill(0).map((group, i) => { + // eslint-disable-line return this.generateSimpleSeries(totalPoints, i, groupPrefix); }); return groups.reduce((acc, curr) => [...acc, ...curr]); diff --git a/src/utils/data_samples/test_dataset.ts b/src/utils/data_samples/test_dataset.ts index 9f758cf1fc..8ef4e9bef6 100644 --- a/src/utils/data_samples/test_dataset.ts +++ b/src/utils/data_samples/test_dataset.ts @@ -101,6 +101,29 @@ export const BARCHART_2Y2G = [ { x: 6, g1: 'cloudflare.com', g2: 'indirect-cdn', y1: 6, y2: 4 }, ]; +export const BARCHART_2Y3G = [ + { x: 0, g1: 'cdn.google.com', g2: 'direct-cdn', y1: 1, y2: 4, g3: 'somevalue' }, + { x: 0, g1: 'cdn.google.com', g2: 'indirect-cdn', y1: 1, y2: 4, g3: 'newvalue' }, + { x: 0, g1: 'cloudflare.com', g2: 'direct-cdn', y1: 3, y2: 6, g3: 'somevalue' }, + { x: 0, g1: 'cloudflare.com', g2: 'indirect-cdn', y1: 3, y2: 6, g3: 'newvalue' }, + { x: 1, g1: 'cdn.google.com', g2: 'direct-cdn', y1: 2, y2: 1, g3: 'somevalue' }, + { x: 1, g1: 'cdn.google.com', g2: 'indirect-cdn', y1: 2, y2: 1, g3: 'newvalue' }, + { x: 1, g1: 'cloudflare.com', g2: 'direct-cdn', y1: 2, y2: 5, g3: 'somevalue' }, + { x: 1, g1: 'cloudflare.com', g2: 'indirect-cdn', y1: 2, y2: 5, g3: 'newvalue' }, + { x: 2, g1: 'cdn.google.com', g2: 'direct-cdn', y1: 10, y2: 5, g3: 'somevalue' }, + { x: 2, g1: 'cdn.google.com', g2: 'indirect-cdn', y1: 10, y2: 5, g3: 'newvalue' }, + { x: 2, g1: 'cloudflare.com', g2: 'direct-cdn', y1: 3, y2: 1, g3: 'somevalue' }, + { x: 2, g1: 'cloudflare.com', g2: 'indirect-cdn', y1: 3, y2: 1, g3: 'newvalue' }, + { x: 3, g1: 'cdn.google.com', g2: 'direct-cdn', y1: 7, y2: 3, g3: 'somevalue' }, + { x: 3, g1: 'cdn.google.com', g2: 'indirect-cdn', y1: 7, y2: 3, g3: 'newvalue' }, + { x: 3, g1: 'cloudflare.com', g2: 'direct-cdn', y1: 6, y2: 4, g3: 'somevalue' }, + { x: 3, g1: 'cloudflare.com', g2: 'indirect-cdn', y1: 6, y2: 4, g3: 'newvalue' }, + { x: 6, g1: 'cdn.google.com', g2: 'direct-cdn', y1: 7, y2: 3, g3: 'somevalue' }, + { x: 6, g1: 'cdn.google.com', g2: 'indirect-cdn', y1: 7, y2: 3, g3: 'newvalue' }, + { x: 6, g1: 'cloudflare.com', g2: 'direct-cdn', y1: 6, y2: 4, g3: 'somevalue' }, + { x: 6, g1: 'cloudflare.com', g2: 'indirect-cdn', y1: 6, y2: 4, g3: 'newvalue' }, +]; + const NOW = Date.now(); const DAY = 24 * 60 * 60 * 1000; export const TIME_CHART_2Y0G = [ diff --git a/src/utils/geometry.ts b/src/utils/geometry.ts index 149322f197..00c21ed340 100644 --- a/src/utils/geometry.ts +++ b/src/utils/geometry.ts @@ -1,26 +1,21 @@ import { $Values } from 'utility-types'; -import { SpecId } from './ids'; import { BarSeriesStyle, PointStyle, AreaStyle, LineStyle, ArcStyle } from './themes/theme'; - -export interface GeometryId { - specId: SpecId; - seriesKey: any[]; -} +import { SeriesIdentifier } from '../chart_types/xy_chart/utils/series'; /** * The accessor type */ -export const AccessorType = Object.freeze({ +export const BandedAccessorType = Object.freeze({ Y0: 'y0' as 'y0', Y1: 'y1' as 'y1', }); -export type AccessorType = $Values; +export type BandedAccessorType = $Values; export interface GeometryValue { y: any; x: any; - accessor: AccessorType; + accessor: BandedAccessorType; } export type IndexedGeometry = PointGeometry | BarGeometry; @@ -41,7 +36,7 @@ export interface PointGeometry { x: number; y: number; }; - geometryId: GeometryId; + seriesIdentifier: SeriesIdentifier; value: GeometryValue; styleOverrides?: Partial; } @@ -58,7 +53,7 @@ export interface BarGeometry { hideClippedValue?: boolean; isValueContainedInElement?: boolean; }; - geometryId: GeometryId; + seriesIdentifier: SeriesIdentifier; value: GeometryValue; seriesStyle: BarSeriesStyle; } @@ -71,7 +66,7 @@ export interface LineGeometry { x: number; y: number; }; - geometryId: GeometryId; + seriesIdentifier: SeriesIdentifier; seriesLineStyle: LineStyle; seriesPointStyle: PointStyle; /** @@ -89,7 +84,7 @@ export interface AreaGeometry { x: number; y: number; }; - geometryId: GeometryId; + seriesIdentifier: SeriesIdentifier; seriesAreaStyle: AreaStyle; seriesAreaLineStyle: LineStyle; seriesPointStyle: PointStyle; @@ -103,7 +98,7 @@ export interface AreaGeometry { export interface ArcGeometry { arc: string; color: string; - geometryId: GeometryId; + seriesIdentifier: SeriesIdentifier; seriesArcStyle: ArcStyle; transform: { x: number; diff --git a/src/utils/themes/theme.ts b/src/utils/themes/theme.ts index 6efc793d5b..2782e4a958 100644 --- a/src/utils/themes/theme.ts +++ b/src/utils/themes/theme.ts @@ -131,7 +131,7 @@ export interface Theme { * * __Note:__ This is not used to set the color of a specific series. As such, any changes to the styles will not be reflected in the tooltip, legend, etc.. * - * You may use `CustomSeriesColorsMap` to assign colors to a given series or replace the `theme.colors.vizColors` colors to your desired colors. + * You may use `CustomSeriesColors` to assign colors to a given series or replace the `theme.colors.vizColors` colors to your desired colors. */ lineSeriesStyle: LineSeriesStyle; /** @@ -139,7 +139,7 @@ export interface Theme { * * __Note:__ This is not used to set the color of a specific series. As such, any changes to the styles will not be reflected in the tooltip, legend, etc.. * - * You may use `CustomSeriesColorsMap` to assign colors to a given series or replace the `theme.colors.vizColors` colors to your desired colors. + * You may use `CustomSeriesColors` to assign colors to a given series or replace the `theme.colors.vizColors` colors to your desired colors. */ areaSeriesStyle: AreaSeriesStyle; /** @@ -147,7 +147,7 @@ export interface Theme { * * __Note:__ This is not used to set the color of a specific series. As such, any changes to the styles will not be reflected in the tooltip, legend, etc.. * - * You may use `CustomSeriesColorsMap` to assign colors to a given series or replace the `theme.colors.vizColors` colors to your desired colors. + * You may use `CustomSeriesColors` to assign colors to a given series or replace the `theme.colors.vizColors` colors to your desired colors. */ barSeriesStyle: BarSeriesStyle; arcSeriesStyle: ArcSeriesStyle; diff --git a/stories/annotations.tsx b/stories/annotations.tsx index 5a38813f7e..e642fc7ce1 100644 --- a/stories/annotations.tsx +++ b/stories/annotations.tsx @@ -13,7 +13,6 @@ import { LineAnnotation, LineAnnotationDatum, LineSeries, - Position, RectAnnotation, ScaleType, Settings, @@ -22,7 +21,8 @@ import { import { Icon } from '../src/components/icons/icon'; import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; import { getChartRotationKnob } from './common'; -import { AccessorType } from '../src/utils/geometry'; +import { BandedAccessorType } from '../src/utils/geometry'; +import { Position } from '../src/chart_types/xy_chart/utils/specs'; const dateFormatter = timeFormatter('HH:mm:ss'); @@ -296,8 +296,8 @@ storiesOf('Annotations', module) { x0: 'x0', x1: 'x1', - y0: AccessorType.Y0, - y1: AccessorType.Y1, + y0: BandedAccessorType.Y0, + y1: BandedAccessorType.Y1, }, 'x0', ); @@ -325,8 +325,8 @@ storiesOf('Annotations', module) coordinates: { x0: definedCoordinate === 'x0' ? 0.25 : null, x1: definedCoordinate === 'x1' ? 2.75 : null, - y0: definedCoordinate === AccessorType.Y0 ? 0.25 : null, - y1: definedCoordinate === AccessorType.Y1 ? 6.75 : null, + y0: definedCoordinate === BandedAccessorType.Y0 ? 0.25 : null, + y1: definedCoordinate === BandedAccessorType.Y1 ? 6.75 : null, }, details: 'can have null values', }, diff --git a/stories/area_chart.tsx b/stories/area_chart.tsx index 6bf938e2b5..1e06189f34 100644 --- a/stories/area_chart.tsx +++ b/stories/area_chart.tsx @@ -16,7 +16,7 @@ import { timeFormatter, } from '../src'; import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; -import { getRandomNumber } from '../.storybook/utils'; +import { getRandomNumber } from '../src/mocks/utils'; const dateFormatter = timeFormatter('HH:mm'); diff --git a/stories/axis.tsx b/stories/axis.tsx index a5a0c1bedd..5df767c934 100644 --- a/stories/axis.tsx +++ b/stories/axis.tsx @@ -19,7 +19,7 @@ import { Settings, niceTimeFormatter, } from '../src/'; -import { SeededDataGenerator } from '../.storybook/utils'; +import { SeededDataGenerator } from '../src/mocks/utils'; import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; function createThemeAction(title: string, min: number, max: number, value: number) { diff --git a/stories/bar_chart.tsx b/stories/bar_chart.tsx index b9c67f1581..4f9e910a02 100644 --- a/stories/bar_chart.tsx +++ b/stories/bar_chart.tsx @@ -26,10 +26,11 @@ import { timeFormatter, TooltipType, } from '../src'; -import { SeededDataGenerator, getRandomNumber } from '../.storybook/utils'; +import { SeededDataGenerator, getRandomNumber } from '../src/mocks/utils'; import * as TestDatasets from '../src/utils/data_samples/test_dataset'; import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana'; import { TEST_DATASET_DISCOVER } from '../src/utils/data_samples/test_dataset_discover_per_30s'; +import { FilterPredicate } from '../src/chart_types/xy_chart/utils/specs'; import { getChartRotationKnob } from './common'; const dateFormatter = timeFormatter('HH:mm:ss'); @@ -799,6 +800,14 @@ storiesOf('Bar Chart', module) ); }) .add('2y2g', () => { + const isVisibleFunction: FilterPredicate = (series) => { + return series.splitAccessors.size > 0 + ? series.specId === getSpecId('bars') && + series.yAccessor === 'y1' && + series.splitAccessors.get('g1') === 'cloudflare.com' + : series.specId === getSpecId('bars') && series.yAccessor === 'y1'; + }; + return ( @@ -816,8 +825,38 @@ storiesOf('Bar Chart', module) yScaleType={ScaleType.Linear} xAccessor="x" yAccessors={['y1', 'y2']} - splitSeriesAccessors={['g1', 'g2']} + splitSeriesAccessors={['g1', 'g2', 'g3']} + data={TestDatasets.BARCHART_2Y2G} + filterSeriesInTooltip={isVisibleFunction} + /> + + ); + }) + .add('tooltip series visibility', () => { + const isVisibleFunction: FilterPredicate = (series) => { + return series.splitAccessors.get('g1') === 'cloudflare.com'; + }; + + return ( + + + + Number(d).toFixed(2)} + /> + + ); diff --git a/stories/legend.tsx b/stories/legend.tsx index 7eea1fa8d9..6014100115 100644 --- a/stories/legend.tsx +++ b/stories/legend.tsx @@ -20,6 +20,9 @@ import { TSVB_DATASET } from '../src/utils/data_samples/test_dataset_tsvb'; storiesOf('Legend', module) .add('right', () => { + const yAccessors = ['y1', 'y2']; + const splitSeriesAccessors = ['g1', 'g2']; + return ( @@ -36,8 +39,8 @@ storiesOf('Legend', module) xScaleType={ScaleType.Linear} yScaleType={ScaleType.Linear} xAccessor="x" - yAccessors={['y1', 'y2']} - splitSeriesAccessors={['g1', 'g2']} + yAccessors={yAccessors} + splitSeriesAccessors={splitSeriesAccessors} data={TestDatasets.BARCHART_2Y2G} hideInLegend={false} /> diff --git a/stories/styling.tsx b/stories/styling.tsx index d176d5ae50..c563a8e89f 100644 --- a/stories/styling.tsx +++ b/stories/styling.tsx @@ -9,8 +9,7 @@ import { BarSeries, Chart, CurveType, - CustomSeriesColorsMap, - DataSeriesColorsValues, + CustomSeriesColors, DEFAULT_MISSING_COLOR, getAxisId, getSpecId, @@ -28,10 +27,17 @@ import { BarSeriesStyle, PointStyle, } from '../src/'; -import { SeededDataGenerator } from '../.storybook/utils'; +import { SeededDataGenerator } from '../src/mocks/utils'; import * as TestDatasets from '../src/utils/data_samples/test_dataset'; import { palettes } from '../src/utils/themes/colors'; -import { BarStyleAccessor, PointStyleAccessor } from '../src/chart_types/xy_chart/utils/specs'; +import { + BarStyleAccessor, + PointStyleAccessor, + SeriesStringPredicate, + SubSeriesStringPredicate, +} from '../src/chart_types/xy_chart/utils/specs'; +import moment from 'moment'; +import { DateTime } from 'luxon'; function range(title: string, min: number, max: number, value: number, groupId?: string, step = 1) { return number( @@ -581,33 +587,57 @@ storiesOf('Stylings', module) info: 'Notice that the secondary theme bar fill has no effect as the primary value takes priority', }, ) - .add('custom series colors through spec props', () => { - const barCustomSeriesColors: CustomSeriesColorsMap = new Map(); - const barDataSeriesColorValues: DataSeriesColorsValues = { - colorValues: ['cloudflare.com', 'direct-cdn', 'y2'], - specId: getSpecId('bars'), - }; + .add('custom series colors via colors array', () => { + return ( + + + + Number(d).toFixed(2)} + /> - const lineCustomSeriesColors: CustomSeriesColorsMap = new Map(); - const lineDataSeriesColorValues: DataSeriesColorsValues = { - colorValues: [], - specId: getSpecId('lines'), + + + ); + }) + .add('custom series colors via accessor function', () => { + const barColor = color('barSeriesColor', '#000'); + const barSeriesColorAccessor: CustomSeriesColors = ({ specId, yAccessor, splitAccessors }) => { + if ( + specId === getSpecId('bars') && + yAccessor === 'y1' && + splitAccessors.get('g1') === 'cloudflare.com' && + splitAccessors.get('g2') === 'direct-cdn' + ) { + return barColor; + } + return null; }; - const customBarColorKnob = color('barDataSeriesColor', '#000'); - const customLineColorKnob = color('lineDataSeriesColor', '#ff0'); - barCustomSeriesColors.set(barDataSeriesColorValues, customBarColorKnob); - lineCustomSeriesColors.set(lineDataSeriesColorValues, customLineColorKnob); + const lineColor = color('linelineSeriesColor', '#ff0'); + const lineSeriesColorAccessor: CustomSeriesColors = ({ specId, yAccessor, splitAccessors }) => { + if (specId === getSpecId('lines') && yAccessor === 'y1' && splitAccessors.size === 0) { + return lineColor; + } + return null; + }; return ( - + @@ -821,6 +851,112 @@ storiesOf('Stylings', module) ); }) + .add('Add custom full and sub series label', () => { + const customSeriesLabel: SeriesStringPredicate = ({ yAccessor, splitAccessors }) => { + if (yAccessor === 'y1' && splitAccessors.get('g') === 'a') { + return 'replace full series name'; + } + + return null; + }; + const customSubSeriesLabel: SubSeriesStringPredicate = (accessor, key) => { + if (key) { + // split accessor; + if (accessor === 'a') { + return 'replace a(from g)'; + } + } else { + // y accessor; + if (accessor === 'y2') { + return 'replace y2'; + } + } + + return null; + }; + return ( + + + + Number(d).toFixed(2)} + /> + + + + ); + }) + .add('Add custom sub-series label formatting [time/date and percent]', () => { + const start = DateTime.fromISO('2019-01-01T00:00:00.000', { zone: 'utc' }); + const data = [ + { x: 1, y: 3, percent: 0.5, time: start.plus({ month: 1 }).toMillis() }, + { x: 2, y: 6, percent: 0.5, time: start.plus({ month: 2 }).toMillis() }, + { x: 3, y: 20, percent: 0.5, time: start.plus({ month: 3 }).toMillis() }, + { x: 1, y: 9, percent: 0.7, time: start.plus({ month: 1 }).toMillis() }, + { x: 2, y: 13, percent: 0.7, time: start.plus({ month: 2 }).toMillis() }, + { x: 3, y: 14, percent: 0.7, time: start.plus({ month: 3 }).toMillis() }, + { x: 1, y: 15, percent: 0.1, time: start.plus({ month: 1 }).toMillis() }, + { x: 2, y: 18, percent: 1, time: start.plus({ month: 2 }).toMillis() }, + { x: 3, y: 7, percent: 1, time: start.plus({ month: 3 }).toMillis() }, + ]; + const customSubSeriesLabel: SubSeriesStringPredicate = (accessor, key, isTooltip) => { + if (key === 'time') { + // Format time group + if (isTooltip) { + // Format tooltip time to be longer + return moment(accessor).format('ll'); + } + + // Format legend to be shorter + return moment(accessor).format('M/YYYY'); + } + + if (key === 'percent') { + // Format percent group + return `${(accessor as number) * 100}%`; + } + + // don't format yAccessor + return null; + }; + + return ( + + + + Number(d).toFixed(2)} + /> + + + + ); + }) .add('tickLabelPadding both prop and theme', () => { const theme: PartialTheme = { axes: { diff --git a/wiki/overview.md b/wiki/overview.md index 797858c5fd..c997fc6a19 100644 --- a/wiki/overview.md +++ b/wiki/overview.md @@ -126,8 +126,8 @@ export interface SeriesSpec { data: Datum[]; /** The type of series you are looking to render */ seriesType: SeriesTypes; - /** Custom colors for series */ - customSeriesColors?: CustomSeriesColorsMap; + /** Set colors for specific series */ + customSeriesColors?: CustomSeriesColors; /** If the series should appear in the legend * @default false */ @@ -219,7 +219,7 @@ In the case of small multiples, each `SplittedSeries` computes its own x and y d #### BarSeries Each bar `datum` of a **Bar** data series can be assigned a custom color or style with the `styleAccessor` prop. -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. +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 `seriesIdentifier` to identify the series the `datum` belongs to and the raw `datum` to derive conditions against. Return types: - `Color`: Color value as a `string` will set the bar `fill` to that color @@ -229,14 +229,14 @@ Return types: ```ts type BarStyleAccessor = ( datum: RawDataSeriesDatum, - geometryId: GeometryId, + seriesIdentifier: SeriesIdentifier, ) => RecursivePartial | Color | null; ``` #### LineSeries and AreaSeries points Each point `datum` of a **Line** or **Area** data series can be assigned a custom color or style with the `pointStyleAccessor` prop. -The `pointStyleAccessor` prop expects a callback function which will be called on _every_ `datum` in _every_ line or area series with the signiture below. This callback should return a color `string` or a partial `PointStyle`, 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. +The `pointStyleAccessor` prop expects a callback function which will be called on _every_ `datum` in _every_ line or area series with the signiture below. This callback should return a color `string` or a partial `PointStyle`, which will override any series-level styles for the respective `datum`. You are passed `seriesIdentifier` to identify the series the `datum` belongs to and the raw `datum` to derive conditions against. Return types: - `Color`: Color value as a `string` will set the point `stroke` to that color @@ -246,7 +246,7 @@ Return types: ```ts type PointStyleAccessor = ( datum: RawDataSeriesDatum, - geometryId: GeometryId, + seriesIdentifier: SeriesIdentifier, ) => RecursivePartial | Color | null; ``` diff --git a/yarn.lock b/yarn.lock index b992d207ea..fc97b8a9e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15591,10 +15591,10 @@ utila@^0.4.0, utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -utility-types@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.8.0.tgz#eaae1c2520c206b5623a4f07af38e3aa31dc2f06" - integrity sha512-UoKivAmVw5TL6AHuILieOJjIK9ajS0l5gyN5LbJglPuVwzfYBniDhe+3A5+ZBtS0TAHQs5qUxTwj9jXurINrcw== +utility-types@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.9.0.tgz#c322d974e7062ec967077384c3b75077a955d83b" + integrity sha512-bUUsGr7NuI1p/6GpDCI/PUCJ1y5KWO4xul8RWOcmUwXSjLoi6WoV9YLNqBF7lH4RRy/jgmhYCE49eM0bnaijMQ== utils-merge@1.0.1: version "1.0.1"