Skip to content

Commit

Permalink
feat(series): set custom series colors through spec prop (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmacunningham authored Mar 11, 2019
1 parent 0b60cbb commit fb09dc9
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 18 deletions.
4 changes: 2 additions & 2 deletions src/lib/series/series.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,15 +265,15 @@ describe('Series', () => {

const emptyCustomColors = new Map();

const defaultColorMap = getSeriesColorMap(seriesColors, chartColors, emptyCustomColors, specs);
const defaultColorMap = getSeriesColorMap(seriesColors, chartColors, emptyCustomColors);
const expectedDefaultColorMap = new Map();
expectedDefaultColorMap.set('spec1', 'elastic_charts_c1');
expect(defaultColorMap).toEqual(expectedDefaultColorMap);

const customColors: Map<string, string> = new Map();
customColors.set('spec1', 'custom_color');

const customizedColorMap = getSeriesColorMap(seriesColors, chartColors, customColors, specs);
const customizedColorMap = getSeriesColorMap(seriesColors, chartColors, customColors);
const expectedCustomizedColorMap = new Map();
expectedCustomizedColorMap.set('spec1', 'custom_color');
expect(customizedColorMap).toEqual(expectedCustomizedColorMap);
Expand Down
13 changes: 4 additions & 9 deletions src/lib/series/series.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ColorConfig } from '../themes/theme';
import { Accessor } from '../utils/accessor';
import { GroupId, SpecId } from '../utils/ids';
import { splitSpecsByGroupId, YBasicSeriesSpec } from './domains/y_domain';
import { getSeriesColorLabel } from './legend';
import { BasicSeriesSpec, Datum, SeriesAccessors } from './specs';

export interface RawDataSeriesDatum {
Expand Down Expand Up @@ -148,7 +147,7 @@ function getColorValues(
/**
* Get the array of values that forms a series key
*/
function getColorValuesAsString(colorValues: any[], specId: SpecId): string {
export function getColorValuesAsString(colorValues: any[], specId: SpecId): string {
return `specId:{${specId}},colors:{${colorValues}}`;
}

Expand Down Expand Up @@ -392,17 +391,13 @@ export function getSeriesColorMap(
seriesColors: Map<string, DataSeriesColorsValues>,
chartColors: ColorConfig,
customColors: Map<string, string>,
specs: Map<SpecId, BasicSeriesSpec>,
): Map<string, string> {
const seriesColorMap = new Map<string, string>();
let counter = 0;

seriesColors.forEach((value, seriesColorKey) => {
const spec = specs.get(value.specId);
const hasSingleSeries = seriesColors.size === 1;
const seriesLabel = getSeriesColorLabel(value, hasSingleSeries, spec);

const color = (seriesLabel && customColors.get(seriesLabel)) ||
seriesColors.forEach((value: DataSeriesColorsValues, seriesColorKey: string) => {
const customSeriesColor: string | undefined = customColors.get(seriesColorKey);
const color = customSeriesColor ||
chartColors.vizColors[counter % chartColors.vizColors.length];

seriesColorMap.set(
Expand Down
5 changes: 5 additions & 0 deletions src/lib/series/specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Domain } from '../utils/domain';
import { AxisId, GroupId, SpecId } from '../utils/ids';
import { ScaleContinuousType, ScaleType } from '../utils/scales/scales';
import { CurveType } from './curves';
import { DataSeriesColorsValues } from './series';
import { TooltipPosition } from './tooltip';

export type Datum = any;
Expand Down Expand Up @@ -37,8 +38,12 @@ export interface SeriesSpec {
yDomain?: Domain;
/** The type of series you are looking to render */
seriesType: 'bar' | 'line' | 'area' | 'basic';
/** Custom colors for series */
customSeriesColors?: CustomSeriesColorsMap;
}

export type CustomSeriesColorsMap = Map<DataSeriesColorsValues, string>;

export interface SeriesAccessors {
/** The field name of the x value on Datum object */
xAccessor: Accessor;
Expand Down
15 changes: 11 additions & 4 deletions src/state/chart_state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,16 +483,23 @@ describe('Chart Store', () => {
store.computeChart = computeChart;
store.legendItems = [firstLegendItem, secondLegendItem];

const expectedCustomColors = new Map();
expectedCustomColors.set(firstLegendItem.label, 'foo');

store.setSeriesColor(-1, 'foo');
expect(computeChart).not.toBeCalled();
expect(store.customSeriesColors).toEqual(new Map());

store.setSeriesColor(0, 'foo');
expect(computeChart).toBeCalled();
expect(store.customSeriesColors).toEqual(expectedCustomColors);
expect(store.seriesSpecs.get(firstLegendItem.value.specId)).toBeUndefined();

store.addSeriesSpec(spec);
store.setSeriesColor(0, 'foo');
const expectedSpecCustomColorSeries = new Map();
expectedSpecCustomColorSeries.set(firstLegendItem.value, 'foo');
expect(spec.customSeriesColors).toEqual(expectedSpecCustomColorSeries);

store.setSeriesColor(1, 'bar');
expectedSpecCustomColorSeries.set(secondLegendItem.value, 'bar');
expect(spec.customSeriesColors).toEqual(expectedSpecCustomColorSeries);
});

test('can reset selectedDataSeries', () => {
Expand Down
21 changes: 18 additions & 3 deletions src/state/chart_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
getAllDataSeriesColorValues,
getAxesSpecForSpecId,
getLegendItemByIndex,
getUpdatedCustomSeriesColors,
Transform,
updateSelectedDataSeries,
} from './utils';
Expand Down Expand Up @@ -293,8 +294,19 @@ export class ChartStore {
const legendItem = getLegendItemByIndex(this.legendItems, legendItemIndex);

if (legendItem) {
const key = legendItem.label;
this.customSeriesColors.set(key, color);
const { specId } = legendItem.value;

const spec = this.seriesSpecs.get(specId);
if (spec) {
if (spec.customSeriesColors) {
spec.customSeriesColors.set(legendItem.value, color);
} else {
const specCustomSeriesColors = new Map();
spec.customSeriesColors = specCustomSeriesColors;
spec.customSeriesColors.set(legendItem.value, color);
}
}

this.computeChart();
}
});
Expand Down Expand Up @@ -438,6 +450,10 @@ export class ChartStore {
this.selectedDataSeries = getAllDataSeriesColorValues(seriesDomains.seriesColors);
}

// Merge all series spec custom colors with state custom colors map
const updatedCustomSeriesColors = getUpdatedCustomSeriesColors(this.seriesSpecs);
this.customSeriesColors = new Map([...this.customSeriesColors, ...updatedCustomSeriesColors]);

// tslint:disable-next-line:no-console
// console.log({colors: seriesDomains.seriesColors});

Expand All @@ -447,7 +463,6 @@ export class ChartStore {
seriesDomains.seriesColors,
this.chartTheme.colors,
this.customSeriesColors,
this.seriesSpecs,
);

this.legendItems = computeLegend(
Expand Down
33 changes: 33 additions & 0 deletions src/state/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
findSelectedDataSeries,
getAllDataSeriesColorValues,
getLegendItemByIndex,
getUpdatedCustomSeriesColors,
updateSelectedDataSeries,
} from './utils';

Expand Down Expand Up @@ -214,4 +215,36 @@ describe('Chart State utils', () => {

expect(getAllDataSeriesColorValues(colorMap)).toEqual(expected);
});
it('should get an updated customSeriesColor based on specs', () => {
const spec1: BasicSeriesSpec = {
id: getSpecId('spec1'),
groupId: getGroupId('group1'),
seriesType: 'line',
yScaleType: ScaleType.Log,
xScaleType: ScaleType.Linear,
xAccessor: 'x',
yAccessors: ['y'],
yScaleToDataExtent: false,
data: BARCHART_1Y0G,
};

const specs = new Map<SpecId, BasicSeriesSpec>();
specs.set(spec1.id, spec1);

const emptyCustomSeriesColors = getUpdatedCustomSeriesColors(specs);
expect(emptyCustomSeriesColors).toEqual(new Map());

const dataSeriesColorValues = {
specId: spec1.id,
colorValues: ['bar'],
};
spec1.customSeriesColors = new Map();
spec1.customSeriesColors.set(dataSeriesColorValues, 'custom_color');

const updatedCustomSeriesColors = getUpdatedCustomSeriesColors(specs);
const expectedCustomSeriesColors = new Map();
expectedCustomSeriesColors.set('specId:{spec1},colors:{bar}', 'custom_color');

expect(updatedCustomSeriesColors).toEqual(expectedCustomSeriesColors);
});
});
15 changes: 15 additions & 0 deletions src/state/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
DataSeries,
DataSeriesColorsValues,
FormattedDataSeries,
getColorValuesAsString,
getFormattedDataseries,
getSplittedSeries,
RawDataSeries,
Expand Down Expand Up @@ -89,6 +90,20 @@ export function updateSelectedDataSeries(
return updatedSeries;
}

export function getUpdatedCustomSeriesColors(seriesSpecs: Map<SpecId, BasicSeriesSpec>): Map<string, string> {
const updatedCustomSeriesColors = new Map();
seriesSpecs.forEach((spec: BasicSeriesSpec, id: SpecId) => {
if (spec.customSeriesColors) {
spec.customSeriesColors.forEach((color: string, seriesColorValues: DataSeriesColorsValues) => {
const { colorValues, specId } = seriesColorValues;
const seriesLabel = getColorValuesAsString(colorValues, specId);
updatedCustomSeriesColors.set(seriesLabel, color);
});
}
});
return updatedCustomSeriesColors;
}

export function computeSeriesDomains(
seriesSpecs: Map<SpecId, BasicSeriesSpec>,
selectedDataSeries?: DataSeriesColorsValues[] | null,
Expand Down
61 changes: 61 additions & 0 deletions stories/styling.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
ScaleType,
Settings,
} from '../src/';
import { DataSeriesColorsValues } from '../src/lib/series/series';
import { CustomSeriesColorsMap } from '../src/lib/series/specs';
import * as TestDatasets from '../src/lib/series/utils/test_dataset';
import { DEFAULT_MISSING_COLOR } from '../src/lib/themes/theme_commons';

function range(
Expand Down Expand Up @@ -348,4 +351,62 @@ storiesOf('Stylings', module)
/>
</Chart>
);
})
.add('custom series colors through spec props', () => {
const barCustomSeriesColors: CustomSeriesColorsMap = new Map();
const barDataSeriesColorValues: DataSeriesColorsValues = {
colorValues: ['cloudflare.com', 'direct-cdn', 'y2'],
specId: getSpecId('bars'),
};

const lineCustomSeriesColors: CustomSeriesColorsMap = new Map();
const lineDataSeriesColorValues: DataSeriesColorsValues = {
colorValues: [],
specId: getSpecId('lines'),
};

const customBarColorKnob = color('barDataSeriesColor', '#000');
const customLineColorKnob = color('lineDataSeriesColor', '#ff0');
barCustomSeriesColors.set(barDataSeriesColorValues, customBarColorKnob);
lineCustomSeriesColors.set(lineDataSeriesColorValues, customLineColorKnob);

return (
<Chart renderer="canvas" className={'story-chart'}>
<Settings showLegend={true} legendPosition={Position.Right} />
<Axis
id={getAxisId('bottom')}
position={Position.Bottom}
title={'Bottom axis'}
showOverlappingTicks={true}
/>
<Axis
id={getAxisId('left2')}
title={'Left axis'}
position={Position.Left}
tickFormat={(d) => Number(d).toFixed(2)}
/>

<BarSeries
id={getSpecId('bars')}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y1', 'y2']}
splitSeriesAccessors={['g1', 'g2']}
customSeriesColors={barCustomSeriesColors}
data={TestDatasets.BARCHART_2Y2G}
yScaleToDataExtent={false}
/>
<LineSeries
id={getSpecId('lines')}
xScaleType={ScaleType.Linear}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
customSeriesColors={lineCustomSeriesColors}
data={[{ x: 0, y: 3 }, { x: 1, y: 2 }, { x: 2, y: 4 }, { x: 3, y: 10 }]}
yScaleToDataExtent={false}
/>
</Chart>
);
});

0 comments on commit fb09dc9

Please sign in to comment.