Skip to content

Commit 5c35a4d

Browse files
authored
feat: banded legend values (#398 & #408)
- 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
1 parent 963d1ac commit 5c35a4d

File tree

16 files changed

+1326
-1181
lines changed

16 files changed

+1326
-1181
lines changed

src/chart_types/xy_chart/legend/legend.test.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import { computeLegend, getSeriesColorLabel } from './legend';
44
import { DataSeriesColorsValues } from '../utils/series';
55
import { AxisSpec, BasicSeriesSpec, Position } from '../utils/specs';
66

7+
const nullDisplayValue = {
8+
formatted: {
9+
y0: null,
10+
y1: null,
11+
},
12+
raw: {
13+
y0: null,
14+
y1: null,
15+
},
16+
};
717
const colorValues1a = {
818
specId: getSpecId('spec1'),
919
colorValues: [],
@@ -86,7 +96,7 @@ describe('Legends', () => {
8696
isSeriesVisible: true,
8797
isLegendItemVisible: true,
8898
key: 'colorSeries1a',
89-
displayValue: {},
99+
displayValue: nullDisplayValue,
90100
},
91101
];
92102
expect(Array.from(legend.values())).toEqual(expected);
@@ -103,7 +113,7 @@ describe('Legends', () => {
103113
isSeriesVisible: true,
104114
isLegendItemVisible: true,
105115
key: 'colorSeries1a',
106-
displayValue: {},
116+
displayValue: nullDisplayValue,
107117
},
108118
{
109119
color: 'blue',
@@ -112,7 +122,7 @@ describe('Legends', () => {
112122
isSeriesVisible: true,
113123
isLegendItemVisible: true,
114124
key: 'colorSeries1b',
115-
displayValue: {},
125+
displayValue: nullDisplayValue,
116126
},
117127
];
118128
expect(Array.from(legend.values())).toEqual(expected);
@@ -129,7 +139,7 @@ describe('Legends', () => {
129139
isSeriesVisible: true,
130140
isLegendItemVisible: true,
131141
key: 'colorSeries1a',
132-
displayValue: {},
142+
displayValue: nullDisplayValue,
133143
},
134144
{
135145
color: 'green',
@@ -138,7 +148,7 @@ describe('Legends', () => {
138148
isSeriesVisible: true,
139149
isLegendItemVisible: true,
140150
key: 'colorSeries2a',
141-
displayValue: {},
151+
displayValue: nullDisplayValue,
142152
},
143153
];
144154
expect(Array.from(legend.values())).toEqual(expected);
@@ -160,7 +170,7 @@ describe('Legends', () => {
160170
isSeriesVisible: true,
161171
isLegendItemVisible: true,
162172
key: 'colorSeries1a',
163-
displayValue: {},
173+
displayValue: nullDisplayValue,
164174
},
165175
];
166176
expect(Array.from(legend.values())).toEqual(expected);

src/chart_types/xy_chart/legend/legend.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,43 @@
1-
import { getAxesSpecForSpecId } from '../store/utils';
1+
import { getAxesSpecForSpecId, LastValues } from '../store/utils';
22
import { identity } from '../../../utils/commons';
33
import { AxisId, SpecId } from '../../../utils/ids';
44
import {
55
DataSeriesColorsValues,
66
findDataSeriesByColorValues,
77
getSortedDataSeriesColorsValuesMap,
88
} from '../utils/series';
9-
import { AxisSpec, BasicSeriesSpec } from '../utils/specs';
9+
import { AxisSpec, BasicSeriesSpec, Postfixes, isAreaSeriesSpec, isBarSeriesSpec } from '../utils/specs';
10+
import { Y0_ACCESSOR_POSTFIX, Y1_ACCESSOR_POSTFIX } from '../tooltip/tooltip';
1011

11-
export interface LegendItem {
12+
export interface FormatedLastValues {
13+
y0: number | string | null;
14+
y1: number | string | null;
15+
}
16+
17+
export type LegendItem = Postfixes & {
1218
key: string;
1319
color: string;
1420
label: string;
1521
value: DataSeriesColorsValues;
1622
isSeriesVisible?: boolean;
23+
banded?: boolean;
1724
isLegendItemVisible?: boolean;
1825
displayValue: {
19-
raw: any;
20-
formatted: any;
26+
raw: LastValues;
27+
formatted: FormatedLastValues;
2128
};
29+
};
30+
31+
export function getPostfix(spec: BasicSeriesSpec): Postfixes {
32+
if (isAreaSeriesSpec(spec) || isBarSeriesSpec(spec)) {
33+
const { y0AccessorFormat = Y0_ACCESSOR_POSTFIX, y1AccessorFormat = Y1_ACCESSOR_POSTFIX } = spec;
34+
return {
35+
y0AccessorFormat,
36+
y1AccessorFormat,
37+
};
38+
}
39+
40+
return {};
2241
}
2342

2443
export function computeLegend(
@@ -33,10 +52,11 @@ export function computeLegend(
3352
const sortedSeriesColors = getSortedDataSeriesColorsValuesMap(seriesColor);
3453

3554
sortedSeriesColors.forEach((series, key) => {
36-
const spec = specs.get(series.specId);
55+
const { banded, specId, lastValue, colorValues } = series;
56+
const spec = specs.get(specId);
3757
const color = seriesColorMap.get(key) || defaultColor;
3858
const hasSingleSeries = seriesColor.size === 1;
39-
const label = getSeriesColorLabel(series.colorValues, hasSingleSeries, spec);
59+
const label = getSeriesColorLabel(colorValues, hasSingleSeries, spec);
4060
const isSeriesVisible = deselectedDataSeries ? findDataSeriesByColorValues(deselectedDataSeries, series) < 0 : true;
4161

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

52-
legendItems.set(key, {
71+
const legendItem: LegendItem = {
5372
key,
5473
color,
5574
label,
75+
banded,
5676
value: series,
5777
isSeriesVisible,
5878
isLegendItemVisible: !hideInLegend,
5979
displayValue: {
60-
raw: series.lastValue,
61-
formatted: isSeriesVisible ? formatter(series.lastValue) : undefined,
80+
raw: {
81+
y0: lastValue && lastValue.y0 !== null ? lastValue.y0 : null,
82+
y1: lastValue && lastValue.y1 !== null ? lastValue.y1 : null,
83+
},
84+
formatted: {
85+
y0: isSeriesVisible && lastValue && lastValue.y0 !== null ? formatter(lastValue.y0) : null,
86+
y1: isSeriesVisible && lastValue && lastValue.y1 !== null ? formatter(lastValue.y1) : null,
87+
},
6288
},
63-
});
89+
...getPostfix(spec),
90+
};
91+
92+
legendItems.set(key, legendItem);
6493
});
6594
return legendItems;
6695
}

src/chart_types/xy_chart/rendering/rendering.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,14 @@ describe('Rendering utils', () => {
103103
isSeriesVisible: true,
104104
isLegendItemVisible: true,
105105
displayValue: {
106-
raw: '',
107-
formatted: '',
106+
formatted: {
107+
y0: null,
108+
y1: null,
109+
},
110+
raw: {
111+
y0: null,
112+
y1: null,
113+
},
108114
},
109115
};
110116

src/chart_types/xy_chart/store/__snapshots__/utils.test.ts.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ Array [
147147
"initialY0": null,
148148
"initialY1": 1,
149149
"x": 0,
150-
"y0": 0,
150+
"y0": null,
151151
"y1": 1,
152152
},
153153
Object {
@@ -159,7 +159,7 @@ Array [
159159
"initialY0": null,
160160
"initialY1": 2,
161161
"x": 1,
162-
"y0": 0,
162+
"y0": null,
163163
"y1": 2,
164164
},
165165
Object {
@@ -171,7 +171,7 @@ Array [
171171
"initialY0": null,
172172
"initialY1": 3,
173173
"x": 2,
174-
"y0": 0,
174+
"y0": null,
175175
"y1": 3,
176176
},
177177
Object {
@@ -183,7 +183,7 @@ Array [
183183
"initialY0": null,
184184
"initialY1": 4,
185185
"x": 3,
186-
"y0": 0,
186+
"y0": null,
187187
"y1": 4,
188188
},
189189
],

src/chart_types/xy_chart/store/chart_state.test.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { LegendItem } from '../legend/legend';
2-
import { GeometryValue, IndexedGeometry } from '../rendering/rendering';
2+
import { GeometryValue, IndexedGeometry, AccessorType } from '../rendering/rendering';
33
import {
44
AnnotationDomainTypes,
55
AnnotationSpec,
@@ -48,8 +48,14 @@ describe('Chart Store', () => {
4848
colorValues: [],
4949
},
5050
displayValue: {
51-
raw: 'last',
52-
formatted: 'formatted-last',
51+
raw: {
52+
y1: null,
53+
y0: null,
54+
},
55+
formatted: {
56+
y1: 'formatted-last',
57+
y0: null,
58+
},
5359
},
5460
};
5561

@@ -62,8 +68,14 @@ describe('Chart Store', () => {
6268
colorValues: [],
6369
},
6470
displayValue: {
65-
raw: 'last',
66-
formatted: 'formatted-last',
71+
raw: {
72+
y1: null,
73+
y0: null,
74+
},
75+
formatted: {
76+
y1: 'formatted-last',
77+
y0: null,
78+
},
6779
},
6880
};
6981
beforeEach(() => {
@@ -1039,7 +1051,7 @@ describe('Chart Store', () => {
10391051
isHighlighted: false,
10401052
isXValue: true,
10411053
seriesKey: 'headerSeries',
1042-
yAccessor: 'y',
1054+
yAccessor: AccessorType.Y0,
10431055
};
10441056

10451057
store.tooltipData.replace([headerValue]);
@@ -1052,13 +1064,17 @@ describe('Chart Store', () => {
10521064
isHighlighted: false,
10531065
isXValue: false,
10541066
seriesKey: 'seriesKey',
1055-
yAccessor: 'y',
1067+
yAccessor: AccessorType.Y1,
10561068
};
10571069
store.tooltipData.replace([headerValue, tooltipValue]);
10581070

10591071
const expectedTooltipValues = new Map();
1060-
expectedTooltipValues.set('seriesKey', 123);
1061-
expect(store.legendItemTooltipValues.get()).toEqual(expectedTooltipValues);
1072+
expectedTooltipValues.set('seriesKey', {
1073+
y0: undefined,
1074+
y1: 123,
1075+
});
1076+
const t = store.legendItemTooltipValues.get();
1077+
expect(t).toEqual(expectedTooltipValues);
10621078
});
10631079
describe('can determine if crosshair cursor is visible', () => {
10641080
const brushEndListener = (): void => {

src/chart_types/xy_chart/store/utils.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,16 +1203,16 @@ describe('Chart State utils', () => {
12031203
key: 'specId:{bars},colors:{a}',
12041204
color: '#1EA593',
12051205
label: 'a',
1206-
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: 6 },
1207-
displayValue: { raw: 6, formatted: '6.00' },
1206+
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: { y0: null, y1: 6 } },
1207+
displayValue: { raw: { y0: null, y1: 6 }, formatted: { y0: null, y1: '6.00' } },
12081208
isSeriesVisible: false,
12091209
});
12101210
legendItems1.set('specId:{bars},colors:{b}', {
12111211
key: 'specId:{bars},colors:{b}',
12121212
color: '#2B70F7',
12131213
label: 'b',
1214-
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: 2 },
1215-
displayValue: { raw: 2, formatted: '2.00' },
1214+
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: { y0: null, y1: 2 } },
1215+
displayValue: { raw: { y0: null, y1: 2 }, formatted: { y0: null, y1: '2.00' } },
12161216
isSeriesVisible: false,
12171217
});
12181218
expect(isAllSeriesDeselected(legendItems1)).toBe(true);
@@ -1223,16 +1223,16 @@ describe('Chart State utils', () => {
12231223
key: 'specId:{bars},colors:{a}',
12241224
color: '#1EA593',
12251225
label: 'a',
1226-
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: 6 },
1227-
displayValue: { raw: 6, formatted: '6.00' },
1226+
value: { specId: getSpecId('bars'), colorValues: ['a'], lastValue: { y0: null, y1: 6 } },
1227+
displayValue: { raw: { y0: null, y1: 6 }, formatted: { y0: null, y1: '6.00' } },
12281228
isSeriesVisible: true,
12291229
});
12301230
legendItems2.set('specId:{bars},colors:{b}', {
12311231
key: 'specId:{bars},colors:{b}',
12321232
color: '#2B70F7',
12331233
label: 'b',
1234-
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: 2 },
1235-
displayValue: { raw: 2, formatted: '2.00' },
1234+
value: { specId: getSpecId('bars'), colorValues: ['b'], lastValue: { y0: null, y1: 2 } },
1235+
displayValue: { raw: { y0: null, y1: 2 }, formatted: { y0: null, y1: '2.00' } },
12361236
isSeriesVisible: false,
12371237
});
12381238
expect(isAllSeriesDeselected(legendItems2)).toBe(false);

src/chart_types/xy_chart/store/utils.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,28 @@ export function getUpdatedCustomSeriesColors(seriesSpecs: Map<SpecId, BasicSerie
9999
return updatedCustomSeriesColors;
100100
}
101101

102+
export interface LastValues {
103+
y0: number | null;
104+
y1: number | null;
105+
}
106+
102107
export function getLastValues(formattedDataSeries: {
103108
stacked: FormattedDataSeries[];
104109
nonStacked: FormattedDataSeries[];
105-
}): Map<string, number> {
106-
const lastValues = new Map<string, number>();
110+
}): Map<string, LastValues> {
111+
const lastValues = new Map<string, LastValues>();
107112

108113
// we need to get the latest
109114
formattedDataSeries.stacked.forEach((ds) => {
110115
ds.dataSeries.forEach((series) => {
111116
if (series.data.length > 0) {
112117
const last = series.data[series.data.length - 1];
113-
if (last !== null && last.initialY1 !== null) {
114-
lastValues.set(series.seriesColorKey, last.initialY1);
118+
if (last !== null) {
119+
const { initialY1: y1, initialY0: y0 } = last;
120+
121+
if (y1 !== null || y0 !== null) {
122+
lastValues.set(series.seriesColorKey, { y0, y1 });
123+
}
115124
}
116125
}
117126
});
@@ -120,8 +129,11 @@ export function getLastValues(formattedDataSeries: {
120129
ds.dataSeries.forEach((series) => {
121130
if (series.data.length > 0) {
122131
const last = series.data[series.data.length - 1];
123-
if (last !== null && last.initialY1 !== null) {
124-
lastValues.set(series.seriesColorKey, last.initialY1);
132+
if (last !== null) {
133+
const { initialY1: y1, initialY0: y0 } = last;
134+
if (y1 !== null || y0 !== null) {
135+
lastValues.set(series.seriesColorKey, { y0, y1 });
136+
}
125137
}
126138
}
127139
});
@@ -157,11 +169,11 @@ export function computeSeriesDomains(
157169

158170
// we need to get the last values from the formatted dataseries
159171
// because we change the format if we are on percentage mode
160-
const lastValues = getLastValues(formattedDataSeries);
172+
const lastValuesMap = getLastValues(formattedDataSeries);
161173
const updatedSeriesColors = new Map<string, DataSeriesColorsValues>();
162174
seriesColors.forEach((value, key) => {
163-
const lastValue = lastValues.get(key);
164-
const updatedColorSet = {
175+
const lastValue = lastValuesMap.get(key);
176+
const updatedColorSet: DataSeriesColorsValues = {
165177
...value,
166178
lastValue,
167179
};

0 commit comments

Comments
 (0)