Skip to content

Commit

Permalink
feat: banded legend values (#398 & #408)
Browse files Browse the repository at this point in the history
- Allow user to set postfix for upper and lower bound of banded series to distinguish between values sets
- Update `displayValue` type changes, fix defaulting point values from 0 to null.

closes #162
  • Loading branch information
nickofthyme authored Oct 7, 2019
1 parent 963d1ac commit 5c35a4d
Show file tree
Hide file tree
Showing 16 changed files with 1,326 additions and 1,181 deletions.
22 changes: 16 additions & 6 deletions src/chart_types/xy_chart/legend/legend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import { computeLegend, getSeriesColorLabel } from './legend';
import { DataSeriesColorsValues } from '../utils/series';
import { AxisSpec, BasicSeriesSpec, Position } from '../utils/specs';

const nullDisplayValue = {
formatted: {
y0: null,
y1: null,
},
raw: {
y0: null,
y1: null,
},
};
const colorValues1a = {
specId: getSpecId('spec1'),
colorValues: [],
Expand Down Expand Up @@ -86,7 +96,7 @@ describe('Legends', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
key: 'colorSeries1a',
displayValue: {},
displayValue: nullDisplayValue,
},
];
expect(Array.from(legend.values())).toEqual(expected);
Expand All @@ -103,7 +113,7 @@ describe('Legends', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
key: 'colorSeries1a',
displayValue: {},
displayValue: nullDisplayValue,
},
{
color: 'blue',
Expand All @@ -112,7 +122,7 @@ describe('Legends', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
key: 'colorSeries1b',
displayValue: {},
displayValue: nullDisplayValue,
},
];
expect(Array.from(legend.values())).toEqual(expected);
Expand All @@ -129,7 +139,7 @@ describe('Legends', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
key: 'colorSeries1a',
displayValue: {},
displayValue: nullDisplayValue,
},
{
color: 'green',
Expand All @@ -138,7 +148,7 @@ describe('Legends', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
key: 'colorSeries2a',
displayValue: {},
displayValue: nullDisplayValue,
},
];
expect(Array.from(legend.values())).toEqual(expected);
Expand All @@ -160,7 +170,7 @@ describe('Legends', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
key: 'colorSeries1a',
displayValue: {},
displayValue: nullDisplayValue,
},
];
expect(Array.from(legend.values())).toEqual(expected);
Expand Down
53 changes: 41 additions & 12 deletions src/chart_types/xy_chart/legend/legend.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
import { getAxesSpecForSpecId } from '../store/utils';
import { getAxesSpecForSpecId, LastValues } from '../store/utils';
import { identity } from '../../../utils/commons';
import { AxisId, SpecId } from '../../../utils/ids';
import {
DataSeriesColorsValues,
findDataSeriesByColorValues,
getSortedDataSeriesColorsValuesMap,
} from '../utils/series';
import { AxisSpec, BasicSeriesSpec } from '../utils/specs';
import { AxisSpec, BasicSeriesSpec, Postfixes, isAreaSeriesSpec, isBarSeriesSpec } from '../utils/specs';
import { Y0_ACCESSOR_POSTFIX, Y1_ACCESSOR_POSTFIX } from '../tooltip/tooltip';

export interface LegendItem {
export interface FormatedLastValues {
y0: number | string | null;
y1: number | string | null;
}

export type LegendItem = Postfixes & {
key: string;
color: string;
label: string;
value: DataSeriesColorsValues;
isSeriesVisible?: boolean;
banded?: boolean;
isLegendItemVisible?: boolean;
displayValue: {
raw: any;
formatted: any;
raw: LastValues;
formatted: FormatedLastValues;
};
};

export function getPostfix(spec: BasicSeriesSpec): Postfixes {
if (isAreaSeriesSpec(spec) || isBarSeriesSpec(spec)) {
const { y0AccessorFormat = Y0_ACCESSOR_POSTFIX, y1AccessorFormat = Y1_ACCESSOR_POSTFIX } = spec;
return {
y0AccessorFormat,
y1AccessorFormat,
};
}

return {};
}

export function computeLegend(
Expand All @@ -33,10 +52,11 @@ export function computeLegend(
const sortedSeriesColors = getSortedDataSeriesColorsValuesMap(seriesColor);

sortedSeriesColors.forEach((series, key) => {
const spec = specs.get(series.specId);
const { banded, specId, lastValue, colorValues } = series;
const spec = specs.get(specId);
const color = seriesColorMap.get(key) || defaultColor;
const hasSingleSeries = seriesColor.size === 1;
const label = getSeriesColorLabel(series.colorValues, hasSingleSeries, spec);
const label = getSeriesColorLabel(colorValues, hasSingleSeries, spec);
const isSeriesVisible = deselectedDataSeries ? findDataSeriesByColorValues(deselectedDataSeries, series) < 0 : true;

if (!label || !spec) {
Expand All @@ -46,21 +66,30 @@ export function computeLegend(
// Use this to get axis spec w/ tick formatter
const { yAxis } = getAxesSpecForSpecId(axesSpecs, spec.groupId);
const formatter = yAxis ? yAxis.tickFormat : identity;

const { hideInLegend } = spec;

legendItems.set(key, {
const legendItem: LegendItem = {
key,
color,
label,
banded,
value: series,
isSeriesVisible,
isLegendItemVisible: !hideInLegend,
displayValue: {
raw: series.lastValue,
formatted: isSeriesVisible ? formatter(series.lastValue) : undefined,
raw: {
y0: lastValue && lastValue.y0 !== null ? lastValue.y0 : null,
y1: lastValue && lastValue.y1 !== null ? lastValue.y1 : null,
},
formatted: {
y0: isSeriesVisible && lastValue && lastValue.y0 !== null ? formatter(lastValue.y0) : null,
y1: isSeriesVisible && lastValue && lastValue.y1 !== null ? formatter(lastValue.y1) : null,
},
},
});
...getPostfix(spec),
};

legendItems.set(key, legendItem);
});
return legendItems;
}
Expand Down
10 changes: 8 additions & 2 deletions src/chart_types/xy_chart/rendering/rendering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,14 @@ describe('Rendering utils', () => {
isSeriesVisible: true,
isLegendItemVisible: true,
displayValue: {
raw: '',
formatted: '',
formatted: {
y0: null,
y1: null,
},
raw: {
y0: null,
y1: null,
},
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Array [
"initialY0": null,
"initialY1": 1,
"x": 0,
"y0": 0,
"y0": null,
"y1": 1,
},
Object {
Expand All @@ -159,7 +159,7 @@ Array [
"initialY0": null,
"initialY1": 2,
"x": 1,
"y0": 0,
"y0": null,
"y1": 2,
},
Object {
Expand All @@ -171,7 +171,7 @@ Array [
"initialY0": null,
"initialY1": 3,
"x": 2,
"y0": 0,
"y0": null,
"y1": 3,
},
Object {
Expand All @@ -183,7 +183,7 @@ Array [
"initialY0": null,
"initialY1": 4,
"x": 3,
"y0": 0,
"y0": null,
"y1": 4,
},
],
Expand Down
34 changes: 25 additions & 9 deletions src/chart_types/xy_chart/store/chart_state.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LegendItem } from '../legend/legend';
import { GeometryValue, IndexedGeometry } from '../rendering/rendering';
import { GeometryValue, IndexedGeometry, AccessorType } from '../rendering/rendering';
import {
AnnotationDomainTypes,
AnnotationSpec,
Expand Down Expand Up @@ -48,8 +48,14 @@ describe('Chart Store', () => {
colorValues: [],
},
displayValue: {
raw: 'last',
formatted: 'formatted-last',
raw: {
y1: null,
y0: null,
},
formatted: {
y1: 'formatted-last',
y0: null,
},
},
};

Expand All @@ -62,8 +68,14 @@ describe('Chart Store', () => {
colorValues: [],
},
displayValue: {
raw: 'last',
formatted: 'formatted-last',
raw: {
y1: null,
y0: null,
},
formatted: {
y1: 'formatted-last',
y0: null,
},
},
};
beforeEach(() => {
Expand Down Expand Up @@ -1039,7 +1051,7 @@ describe('Chart Store', () => {
isHighlighted: false,
isXValue: true,
seriesKey: 'headerSeries',
yAccessor: 'y',
yAccessor: AccessorType.Y0,
};

store.tooltipData.replace([headerValue]);
Expand All @@ -1052,13 +1064,17 @@ describe('Chart Store', () => {
isHighlighted: false,
isXValue: false,
seriesKey: 'seriesKey',
yAccessor: 'y',
yAccessor: AccessorType.Y1,
};
store.tooltipData.replace([headerValue, tooltipValue]);

const expectedTooltipValues = new Map();
expectedTooltipValues.set('seriesKey', 123);
expect(store.legendItemTooltipValues.get()).toEqual(expectedTooltipValues);
expectedTooltipValues.set('seriesKey', {
y0: undefined,
y1: 123,
});
const t = store.legendItemTooltipValues.get();
expect(t).toEqual(expectedTooltipValues);
});
describe('can determine if crosshair cursor is visible', () => {
const brushEndListener = (): void => {
Expand Down
16 changes: 8 additions & 8 deletions src/chart_types/xy_chart/store/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1203,16 +1203,16 @@ describe('Chart State utils', () => {
key: 'specId:{bars},colors:{a}',
color: '#1EA593',
label: 'a',
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: 6 },
displayValue: { raw: 6, formatted: '6.00' },
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: { y0: null, y1: 6 } },
displayValue: { raw: { y0: null, y1: 6 }, formatted: { y0: null, y1: '6.00' } },
isSeriesVisible: false,
});
legendItems1.set('specId:{bars},colors:{b}', {
key: 'specId:{bars},colors:{b}',
color: '#2B70F7',
label: 'b',
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: 2 },
displayValue: { raw: 2, formatted: '2.00' },
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: { y0: null, y1: 2 } },
displayValue: { raw: { y0: null, y1: 2 }, formatted: { y0: null, y1: '2.00' } },
isSeriesVisible: false,
});
expect(isAllSeriesDeselected(legendItems1)).toBe(true);
Expand All @@ -1223,16 +1223,16 @@ describe('Chart State utils', () => {
key: 'specId:{bars},colors:{a}',
color: '#1EA593',
label: 'a',
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: 6 },
displayValue: { raw: 6, formatted: '6.00' },
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: { y0: null, y1: 6 } },
displayValue: { raw: { y0: null, y1: 6 }, formatted: { y0: null, y1: '6.00' } },
isSeriesVisible: true,
});
legendItems2.set('specId:{bars},colors:{b}', {
key: 'specId:{bars},colors:{b}',
color: '#2B70F7',
label: 'b',
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: 2 },
displayValue: { raw: 2, formatted: '2.00' },
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: { y0: null, y1: 2 } },
displayValue: { raw: { y0: null, y1: 2 }, formatted: { y0: null, y1: '2.00' } },
isSeriesVisible: false,
});
expect(isAllSeriesDeselected(legendItems2)).toBe(false);
Expand Down
30 changes: 21 additions & 9 deletions src/chart_types/xy_chart/store/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,19 +99,28 @@ export function getUpdatedCustomSeriesColors(seriesSpecs: Map<SpecId, BasicSerie
return updatedCustomSeriesColors;
}

export interface LastValues {
y0: number | null;
y1: number | null;
}

export function getLastValues(formattedDataSeries: {
stacked: FormattedDataSeries[];
nonStacked: FormattedDataSeries[];
}): Map<string, number> {
const lastValues = new Map<string, number>();
}): Map<string, LastValues> {
const lastValues = new Map<string, LastValues>();

// we need to get the latest
formattedDataSeries.stacked.forEach((ds) => {
ds.dataSeries.forEach((series) => {
if (series.data.length > 0) {
const last = series.data[series.data.length - 1];
if (last !== null && last.initialY1 !== null) {
lastValues.set(series.seriesColorKey, last.initialY1);
if (last !== null) {
const { initialY1: y1, initialY0: y0 } = last;

if (y1 !== null || y0 !== null) {
lastValues.set(series.seriesColorKey, { y0, y1 });
}
}
}
});
Expand All @@ -120,8 +129,11 @@ export function getLastValues(formattedDataSeries: {
ds.dataSeries.forEach((series) => {
if (series.data.length > 0) {
const last = series.data[series.data.length - 1];
if (last !== null && last.initialY1 !== null) {
lastValues.set(series.seriesColorKey, last.initialY1);
if (last !== null) {
const { initialY1: y1, initialY0: y0 } = last;
if (y1 !== null || y0 !== null) {
lastValues.set(series.seriesColorKey, { y0, y1 });
}
}
}
});
Expand Down Expand Up @@ -157,11 +169,11 @@ 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 lastValues = getLastValues(formattedDataSeries);
const lastValuesMap = getLastValues(formattedDataSeries);
const updatedSeriesColors = new Map<string, DataSeriesColorsValues>();
seriesColors.forEach((value, key) => {
const lastValue = lastValues.get(key);
const updatedColorSet = {
const lastValue = lastValuesMap.get(key);
const updatedColorSet: DataSeriesColorsValues = {
...value,
lastValue,
};
Expand Down
Loading

0 comments on commit 5c35a4d

Please sign in to comment.