diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/interactions/sunburst-slice-clicks-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/interactions/sunburst-slice-clicks-chrome-linux.png index c628917dbe..b42afeaa3b 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/interactions/sunburst-slice-clicks-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/interactions/sunburst-slice-clicks-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/legend/piechart-repeated-labels-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/legend/piechart-repeated-labels-chrome-linux.png index 7fe8a1e6e6..ae0ad40f8d 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/legend/piechart-repeated-labels-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/legend/piechart-repeated-labels-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/mosaic-alpha/simple-mosaic-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/mosaic-alpha/simple-mosaic-chrome-linux.png index a1e7203f52..09289071d8 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/mosaic-alpha/simple-mosaic-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/mosaic-alpha/simple-mosaic-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/custom-tooltip-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/custom-tooltip-chrome-linux.png index f8677deff9..15dc2c4a07 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/custom-tooltip-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/custom-tooltip-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/sunburst-with-two-layers-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/sunburst-with-two-layers-chrome-linux.png index f8677deff9..15dc2c4a07 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/sunburst-with-two-layers-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/sunburst/sunburst-with-two-layers-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/treemap/mid-two-layers-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/treemap/mid-two-layers-chrome-linux.png index 3c23646fb8..7ea4cd6f23 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/treemap/mid-two-layers-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/treemap/mid-two-layers-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/simple-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/simple-chrome-linux.png index 38b06d2897..d7513fc17b 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/simple-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/simple-chrome-linux.png differ diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/test-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/test-chrome-linux.png index b6fe13f45f..483e8e21fd 100644 Binary files a/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/test-chrome-linux.png and b/e2e/screenshots/all.test.ts-snapshots/baselines/waffle-alpha/test-chrome-linux.png differ diff --git a/e2e/screenshots/interactions.test.ts-snapshots/interactions/tooltips/should-show-tooltip-on-sunburst-chrome-linux.png b/e2e/screenshots/interactions.test.ts-snapshots/interactions/tooltips/should-show-tooltip-on-sunburst-chrome-linux.png index 42f1ccdc08..55fa9113d2 100644 Binary files a/e2e/screenshots/interactions.test.ts-snapshots/interactions/tooltips/should-show-tooltip-on-sunburst-chrome-linux.png and b/e2e/screenshots/interactions.test.ts-snapshots/interactions/tooltips/should-show-tooltip-on-sunburst-chrome-linux.png differ diff --git a/e2e/screenshots/legend_stories.test.ts-snapshots/legend-stories/should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-chrome-linux.png b/e2e/screenshots/legend_stories.test.ts-snapshots/legend-stories/should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-chrome-linux.png index fda7e330b9..304165d741 100644 Binary files a/e2e/screenshots/legend_stories.test.ts-snapshots/legend-stories/should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-chrome-linux.png and b/e2e/screenshots/legend_stories.test.ts-snapshots/legend-stories/should-have-the-same-order-as-nested-with-no-indent-even-if-there-are-repeated-labels-chrome-linux.png differ diff --git a/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/no-stroke-artifacts-when-using-semi-transparent-colors-chrome-linux.png b/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/no-stroke-artifacts-when-using-semi-transparent-colors-chrome-linux.png index ae36856da7..ae05b19385 100644 Binary files a/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/no-stroke-artifacts-when-using-semi-transparent-colors-chrome-linux.png and b/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/no-stroke-artifacts-when-using-semi-transparent-colors-chrome-linux.png differ diff --git a/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/should-render-cells-in-ascending-order-chrome-linux.png b/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/should-render-cells-in-ascending-order-chrome-linux.png index 6e366e2415..5b3da2ea1e 100644 Binary files a/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/should-render-cells-in-ascending-order-chrome-linux.png and b/e2e/screenshots/waffle_stories.test.ts-snapshots/waffle/should-render-cells-in-ascending-order-chrome-linux.png differ diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index 99eb3c6bd5..4d51b48d69 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -858,10 +858,7 @@ export interface CustomLegendProps { label: CategoryLabel; seriesType?: SeriesType; pointStyle?: PointStyle; - extraValue?: { - raw: PrimitiveValue; - formatted: string; - }; + extraValue?: LegendItemValue; isSeriesHidden?: boolean; onItemOverActon: () => void; onItemOutAction: () => void; @@ -1816,6 +1813,12 @@ export interface LegendColorPickerProps { // @public (undocumented) export type LegendItemListener = (series: SeriesIdentifier[]) => void; +// @public (undocumented) +export type LegendItemValue = { + value: PrimitiveValue; + label: string; +}; + // @public (undocumented) export interface LegendLabelOptions { maxLines: number; @@ -1851,6 +1854,7 @@ export interface LegendSpec { legendSize: number; legendSort?: SeriesCompareFn; legendStrategy?: LegendStrategy; + legendValues: Array; // (undocumented) onLegendItemClick?: LegendItemListener; // (undocumented) @@ -1862,7 +1866,6 @@ export interface LegendSpec { // (undocumented) onLegendItemPlusClick?: LegendItemListener; showLegend: boolean; - showLegendExtra: boolean; } // @public (undocumented) @@ -1887,6 +1890,33 @@ export interface LegendStyle { verticalWidth: number; } +// @public (undocumented) +export const LegendValue: Readonly<{ + None: "none"; + CurrentAndLastValue: "currentAndLastValue"; + LastValue: "lastValue"; + LastNonNullValue: "lastNonNullValue"; + Average: "average"; + Median: "median"; + Max: "max"; + Min: "min"; + FirstValue: "firstValue"; + FirstNonNullValue: "firstNonNullValue"; + Total: "total"; + Count: "count"; + DistinctCount: "distinctCount"; + Variance: "variance"; + StdDeviation: "stdDeviation"; + Range: "range"; + Difference: "difference"; + DifferencePercent: "differencePercent"; + Value: "value"; + Percent: "percent"; +}>; + +// @public (undocumented) +export type LegendValue = $Values; + // @public export const LIGHT_BASE_COLORS: ChartBaseColors; @@ -2708,7 +2738,7 @@ export const Settings: (props: SFProps; +export const settingsBuildProps: BuildProps; // @public (undocumented) export type SettingsProps = ComponentProps; diff --git a/packages/charts/src/chart_types/heatmap/state/selectors/compute_legend.ts b/packages/charts/src/chart_types/heatmap/state/selectors/compute_legend.ts index 11a47a60cc..b553be8e52 100644 --- a/packages/charts/src/chart_types/heatmap/state/selectors/compute_legend.ts +++ b/packages/charts/src/chart_types/heatmap/state/selectors/compute_legend.ts @@ -26,12 +26,14 @@ export const computeLegendSelector = createCustomCachedSelector( return { // the band label is considered unique by construction seriesIdentifiers: [{ key: label, specId: label }], + depth: 0, color, label, isSeriesHidden: deselectedDataSeries.some(({ key }) => key === label), isToggleable: true, path: [{ index: 0, value: label }], keys: [], + values: [], }; }); }, diff --git a/packages/charts/src/chart_types/heatmap/state/selectors/get_legend_items_labels.ts b/packages/charts/src/chart_types/heatmap/state/selectors/get_legend_items_labels.ts index dae202929f..2e758de3c7 100644 --- a/packages/charts/src/chart_types/heatmap/state/selectors/get_legend_items_labels.ts +++ b/packages/charts/src/chart_types/heatmap/state/selectors/get_legend_items_labels.ts @@ -14,9 +14,11 @@ import { getSettingsSpecSelector } from '../../../../state/selectors/get_setting /** @internal */ export const getLegendItemsLabelsSelector = createCustomCachedSelector( [computeLegendSelector, getSettingsSpecSelector], - (legendItems, { showLegendExtra }): LegendItemLabel[] => - legendItems.map(({ label, defaultExtra }) => ({ - label: `${label}${showLegendExtra ? defaultExtra?.formatted ?? '' : ''}`, - depth: 0, - })), + (legendItems, { legendValues }): LegendItemLabel[] => + legendItems.map(({ label, values }) => { + return { + label: `${label}${legendValues.length > 0 ? values[0]?.label ?? '' : ''}`, + depth: 0, + }; + }), ); diff --git a/packages/charts/src/chart_types/partition_chart/__snapshots__/partition.test.tsx.snap b/packages/charts/src/chart_types/partition_chart/__snapshots__/partition.test.tsx.snap index 11a5d6ef39..ceb5f5d1f1 100644 --- a/packages/charts/src/chart_types/partition_chart/__snapshots__/partition.test.tsx.snap +++ b/packages/charts/src/chart_types/partition_chart/__snapshots__/partition.test.tsx.snap @@ -5,11 +5,6 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct { "childId": "A", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "2", - "legendSizingLabel": "2", - "raw": 2, - }, "depth": 0, "keys": [], "label": "A", @@ -33,15 +28,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "2", + "value": 2, + }, + ], }, { "childId": "A", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "A", @@ -69,15 +65,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "B", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "B", @@ -105,15 +102,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "B", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "2", - "legendSizingLabel": "2", - "raw": 2, - }, "depth": 0, "keys": [], "label": "B", @@ -137,15 +135,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "2", + "value": 2, + }, + ], }, { "childId": "A", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "A", @@ -173,15 +172,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "B", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "B", @@ -209,15 +209,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "C", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "2", - "legendSizingLabel": "2", - "raw": 2, - }, "depth": 0, "keys": [], "label": "C", @@ -241,15 +242,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "2", + "value": 2, + }, + ], }, { "childId": "A", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "A", @@ -277,15 +279,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "B", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "B", @@ -313,6 +316,12 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, ] `; @@ -322,11 +331,6 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: { "childId": "A", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 0, "keys": [], "label": "A", @@ -350,15 +354,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "A", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "A", @@ -386,6 +391,12 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, ] `; @@ -395,11 +406,6 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: { "childId": "C", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 0, "keys": [], "label": "C", @@ -423,15 +429,16 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, { "childId": "B", "color": "rgba(128, 0, 0, 0.5)", - "defaultExtra": { - "formatted": "1", - "legendSizingLabel": "1", - "raw": 1, - }, "depth": 1, "keys": [], "label": "B", @@ -459,6 +466,12 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "specId": "spec1", }, ], + "values": [ + { + "label": "1", + "value": 1, + }, + ], }, ] `; diff --git a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts index 40e2fdcf08..86c7a20649 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/viewmodel/hierarchy_of_arrays.ts @@ -117,8 +117,8 @@ export function getExtraValueMap( const [key, arrayNode] = branch; const { value, path, [CHILDREN_KEY]: children } = arrayNode; const values: LegendItemExtraValues = new Map(); - const formattedValue = valueFormatter ? valueFormatter(value) : `${value}`; - values.set(key, { formatted: formattedValue, raw: value }); + const label = valueFormatter ? valueFormatter(value) : `${value}`; + values.set(key, { label, value }); keys.set(path.map(({ index }) => index).join('__'), values); if (depth < maxDepth) getExtraValueMap(layers, valueFormatter, children, maxDepth, depth + 1, keys); } diff --git a/packages/charts/src/chart_types/partition_chart/state/chart_state.tsx b/packages/charts/src/chart_types/partition_chart/state/chart_state.tsx index 1707e10a9c..034000447c 100644 --- a/packages/charts/src/chart_types/partition_chart/state/chart_state.tsx +++ b/packages/charts/src/chart_types/partition_chart/state/chart_state.tsx @@ -12,7 +12,6 @@ import { computeLegendSelector } from './selectors/compute_legend'; import { getChartTypeDescriptionSelector } from './selectors/get_chart_type_description'; import { getPointerCursorSelector } from './selectors/get_cursor_pointer'; import { getDebugStateSelector } from './selectors/get_debug_state'; -import { getLegendItemsExtra } from './selectors/get_legend_items_extra'; import { getLegendItemsLabels } from './selectors/get_legend_items_labels'; import { isTooltipVisibleSelector } from './selectors/is_tooltip_visible'; import { createOnElementClickCaller } from './selectors/on_element_click_caller'; @@ -21,6 +20,8 @@ import { createOnElementOverCaller } from './selectors/on_element_over_caller'; import { getPartitionSpec } from './selectors/partition_spec'; import { getTooltipInfoSelector } from './selectors/tooltip'; import { ChartType } from '../..'; +import { LegendItemExtraValues } from '../../../common/legend'; +import { SeriesKey } from '../../../common/series_id'; import { BackwardRef, GlobalChartState, InternalChartState } from '../../../state/chart_state'; import { getActivePointerPosition } from '../../../state/selectors/get_active_pointer_position'; import { InitStatus } from '../../../state/selectors/get_internal_is_intialized'; @@ -28,6 +29,8 @@ import { DebugState } from '../../../state/types'; import { Dimensions } from '../../../utils/dimensions'; import { render } from '../renderer/dom/layered_partition_chart'; +const EMPTY_MAP = new Map(); + /** @internal */ export class PartitionState implements InternalChartState { chartType = ChartType.Partition; @@ -71,8 +74,8 @@ export class PartitionState implements InternalChartState { return computeLegendSelector(globalState); } - getLegendExtraValues(globalState: GlobalChartState) { - return getLegendItemsExtra(globalState); + getLegendExtraValues() { + return EMPTY_MAP; } chartRenderer(containerRef: BackwardRef, forwardStageRef: RefObject) { diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/__snapshots__/get_legend_items_extra.test.ts.snap b/packages/charts/src/chart_types/partition_chart/state/selectors/__snapshots__/get_legend_items_extra.test.ts.snap deleted file mode 100644 index 6425c0c73d..0000000000 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/__snapshots__/get_legend_items_extra.test.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Partition - Legend item extra values should return all extra values in nested legend 1`] = `{}`; - -exports[`Partition - Legend item extra values should return extra values in nested legend within max depth of 1 1`] = `{}`; - -exports[`Partition - Legend item extra values should return extra values in nested legend within max depth of 2 1`] = `{}`; diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/compute_legend.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/compute_legend.ts index 3d70692861..06dcd513d8 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/compute_legend.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/compute_legend.ts @@ -123,11 +123,12 @@ function walkTree( depth: node[DEPTH_KEY] - 1, seriesIdentifiers: [{ key, specId }], keys: [], - defaultExtra: { - raw: node[AGGREGATE_KEY], - formatted: valueFormatter(node[AGGREGATE_KEY]), - legendSizingLabel: `${node[AGGREGATE_KEY]}`, - }, + values: [ + { + value: node[AGGREGATE_KEY], + label: valueFormatter(node[AGGREGATE_KEY]), + }, + ], }, node, }); diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts deleted file mode 100644 index df72699a71..0000000000 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Store } from 'redux'; - -import { getLegendItemsExtra } from './get_legend_items_extra'; -import { MockSeriesSpec, MockGlobalSpec } from '../../../../mocks/specs'; -import { MockStore } from '../../../../mocks/store'; -import { GlobalChartState } from '../../../../state/chart_state'; -import { PrimitiveValue } from '../../layout/utils/group_by_rollup'; - -describe('Partition - Legend item extra values', () => { - type TestDatum = [string, string, string, number]; - const spec = MockSeriesSpec.sunburst({ - data: [ - ['aaa', 'aa', '1', 1], - ['aaa', 'aa', '1', 2], - ['aaa', 'aa', '3', 1], - ['aaa', 'bb', '4', 1], - ['aaa', 'bb', '5', 1], - ['aaa', 'bb', '6', 1], - ['bbb', 'aa', '7', 1], - ['bbb', 'aa', '8', 1], - ['bbb', 'bb', '9', 1], - ['bbb', 'bb', '10', 1], - ['bbb', 'cc', '11', 1], - ['bbb', 'cc', '12', 1], - ], - valueAccessor: (d: TestDatum) => d[3], - layers: [ - { - groupByRollup: (datum: TestDatum) => datum[0], - nodeLabel: (d: PrimitiveValue) => String(d), - }, - { - groupByRollup: (datum: TestDatum) => datum[1], - nodeLabel: (d: PrimitiveValue) => String(d), - }, - { - groupByRollup: (datum: TestDatum) => datum[2], - nodeLabel: (d: PrimitiveValue) => String(d), - }, - ], - }); - let store: Store; - - beforeEach(() => { - store = MockStore.default(); - }); - - it('should return all extra values in nested legend', () => { - MockStore.addSpecs([spec], store); - - const extraValues = getLegendItemsExtra(store.getState()); - expect([...extraValues.keys()]).toEqual([ - '0__0', - '0__0__0', - '0__0__0__0', - '0__0__0__0__0', - '0__0__0__0__1', - '0__0__0__1', - '0__0__0__1__0', - '0__0__0__1__1', - '0__0__0__1__2', - '0__0__1', - '0__0__1__0', - '0__0__1__0__0', - '0__0__1__0__1', - '0__0__1__1', - '0__0__1__1__0', - '0__0__1__1__1', - '0__0__1__2', - '0__0__1__2__0', - '0__0__1__2__1', - ]); - expect(extraValues.values()).toMatchSnapshot(); - }); - - it('should return extra values in nested legend within max depth of 1', () => { - const settings = MockGlobalSpec.settings({ legendMaxDepth: 1 }); - MockStore.addSpecs([settings, spec], store); - - const extraValues = getLegendItemsExtra(store.getState()); - expect([...extraValues.keys()]).toEqual(['0__0', '0__0__0', '0__0__1']); - expect(extraValues.values()).toMatchSnapshot(); - }); - - it('should return extra values in nested legend within max depth of 2', () => { - const settings = MockGlobalSpec.settings({ legendMaxDepth: 2 }); - MockStore.addSpecs([settings, spec], store); - - const extraValues = getLegendItemsExtra(store.getState()); - expect([...extraValues.keys()]).toEqual([ - '0__0', - '0__0__0', - '0__0__0__0', - '0__0__0__1', - '0__0__1', - '0__0__1__0', - '0__0__1__1', - '0__0__1__2', - ]); - expect(extraValues.values()).toMatchSnapshot(); - }); - - it('filters all extraValues if depth is 0', () => { - const settings = MockGlobalSpec.settings({ legendMaxDepth: 0 }); - MockStore.addSpecs([settings, spec], store); - - const extraValues = getLegendItemsExtra(store.getState()); - expect([...extraValues.keys()]).toEqual([]); - }); - - it('filters all extraValues if depth is NaN', () => { - const settings = MockGlobalSpec.settings({ legendMaxDepth: NaN }); - MockStore.addSpecs([settings, spec], store); - - const extraValues = getLegendItemsExtra(store.getState()); - expect([...extraValues.keys()]).toEqual([]); - }); -}); diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts deleted file mode 100644 index 22c4957de2..0000000000 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_extra.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { getPartitionSpec } from './partition_spec'; -import { getTrees } from './tree'; -import { LegendItemExtraValues } from '../../../../common/legend'; -import { SeriesKey } from '../../../../common/series_id'; -import { createCustomCachedSelector } from '../../../../state/create_selector'; -import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_spec'; -import { getExtraValueMap } from '../../layout/viewmodel/hierarchy_of_arrays'; - -/** @internal */ -export const getLegendItemsExtra = createCustomCachedSelector( - [getPartitionSpec, getSettingsSpecSelector, getTrees], - (spec, { legendMaxDepth }, trees): Map => { - const emptyMap = new Map(); - return spec && !Number.isNaN(legendMaxDepth) && legendMaxDepth > 0 - ? trees.reduce((result, { tree }) => { - const treeData = getExtraValueMap(spec.layers, spec.valueFormatter, tree, legendMaxDepth); - for (const [key, value] of treeData) { - result.set(key, value); - } - return result; - }, emptyMap) - : emptyMap; - }, -); diff --git a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_labels.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_labels.ts index a52bed4f60..83b13d7e8d 100644 --- a/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_labels.ts +++ b/packages/charts/src/chart_types/partition_chart/state/selectors/get_legend_items_labels.ts @@ -16,11 +16,11 @@ import { getLegendLabelsAndValue } from '../../layout/utils/legend_labels'; /** @internal */ export const getLegendItemsLabels = createCustomCachedSelector( [getPartitionSpecs, getSettingsSpecSelector, getTrees], - (specs, { legendMaxDepth, showLegend, showLegendExtra }, trees): LegendItemLabel[] => + (specs, { legendMaxDepth, showLegend, legendValues }, trees): LegendItemLabel[] => specs.flatMap(({ layers, valueFormatter }) => showLegend ? trees.flatMap(({ tree }) => - getLegendLabelsAndValue(layers, tree, legendMaxDepth, showLegendExtra ? valueFormatter : () => ''), + getLegendLabelsAndValue(layers, tree, legendMaxDepth, legendValues.length > 0 ? valueFormatter : () => ''), ) : [], ), diff --git a/packages/charts/src/chart_types/xy_chart/legend/legend.test.ts b/packages/charts/src/chart_types/xy_chart/legend/legend.test.ts index 7b169e54da..0b8f5ff001 100644 --- a/packages/charts/src/chart_types/xy_chart/legend/legend.test.ts +++ b/packages/charts/src/chart_types/xy_chart/legend/legend.test.ts @@ -22,8 +22,6 @@ import { computeSeriesDomainsSelector } from '../state/selectors/compute_series_ import { getSeriesName } from '../utils/series'; import { AxisSpec, BasicSeriesSpec, SeriesType } from '../utils/specs'; -const nullDisplayValue = undefined; - const spec1: BasicSeriesSpec = { chartType: ChartType.XYAxis, specType: SpecType.Series, @@ -100,7 +98,7 @@ describe('Legends', () => { isItemHidden: false, isSeriesHidden: false, isToggleable: true, - defaultExtra: nullDisplayValue, + values: [], path: [{ index: 0, value: 'groupId{__global__}spec{spec1}yAccessor{y1}splitAccessors{}' }], }; expect(legend[0]).toMatchObject(expected); @@ -143,7 +141,7 @@ describe('Legends', () => { isItemHidden: false, isSeriesHidden: false, isToggleable: true, - defaultExtra: nullDisplayValue, + values: [], path: [{ index: 0, value: 'groupId{__global__}spec{spec1}yAccessor{y1}splitAccessors{g-a}' }], }, { @@ -153,7 +151,7 @@ describe('Legends', () => { isItemHidden: false, isSeriesHidden: false, isToggleable: true, - defaultExtra: nullDisplayValue, + values: [], path: [{ index: 0, value: 'groupId{__global__}spec{spec1}yAccessor{y2}splitAccessors{g-a}' }], }, { @@ -163,7 +161,7 @@ describe('Legends', () => { isItemHidden: false, isSeriesHidden: false, isToggleable: true, - defaultExtra: nullDisplayValue, + values: [], path: [{ index: 0, value: 'groupId{__global__}spec{spec1}yAccessor{y1}splitAccessors{g-b}' }], }, { @@ -173,7 +171,7 @@ describe('Legends', () => { isItemHidden: false, isSeriesHidden: false, isToggleable: true, - defaultExtra: nullDisplayValue, + values: [], path: [{ index: 0, value: 'groupId{__global__}spec{spec1}yAccessor{y2}splitAccessors{g-b}' }], }, ]; @@ -222,7 +220,7 @@ describe('Legends', () => { isItemHidden: false, isSeriesHidden: false, isToggleable: true, - defaultExtra: nullDisplayValue, + values: [], path: [{ index: 0, value: 'groupId{__global__}spec{spec2}yAccessor{y}splitAccessors{}' }], }, ]; diff --git a/packages/charts/src/chart_types/xy_chart/legend/legend.ts b/packages/charts/src/chart_types/xy_chart/legend/legend.ts index fa3d6c68ec..98bc2144e5 100644 --- a/packages/charts/src/chart_types/xy_chart/legend/legend.ts +++ b/packages/charts/src/chart_types/xy_chart/legend/legend.ts @@ -7,7 +7,7 @@ */ import { Color } from '../../../common/colors'; -import { LegendItem } from '../../../common/legend'; +import { LegendItem, LegendValue } from '../../../common/legend'; import { SeriesKey, SeriesIdentifier } from '../../../common/series_id'; import { SettingsSpec } from '../../../specs'; import { isDefined, mergePartial } from '../../../utils/common'; @@ -15,7 +15,8 @@ import { BandedAccessorType } from '../../../utils/geometry'; import { getLegendCompareFn, SeriesCompareFn } from '../../../utils/series_sort'; import { PointStyle, Theme } from '../../../utils/themes/theme'; import { XDomain } from '../domains/types'; -import { LegendValue, getLegendValue } from '../state/utils/get_last_value'; +import { isDatumFilled } from '../rendering/utils'; +import { getLegendValue } from '../state/utils/get_last_value'; import { getAxesSpecForSpecId, getSpecsById } from '../state/utils/spec'; import { Y0_ACCESSOR_POSTFIX, Y1_ACCESSOR_POSTFIX } from '../tooltip/tooltip'; import { defaultTickFormatter } from '../utils/axis_utils'; @@ -28,6 +29,7 @@ import { getSeriesKey, getSeriesIdentifierFromDataSeries, isBandedSpec, + DataSeriesDatum, } from '../utils/series'; import { AxisSpec, @@ -72,6 +74,26 @@ function getPointStyle(spec: BasicSeriesSpec, theme: Theme): PointStyle | undefi } } +const y1Accessor = + (stackMode?: StackMode) => + (d: DataSeriesDatum): number | null => { + // don't consider filled in data in the calculations + if (isDatumFilled(d)) { + return null; + } + return stackMode === StackMode.Percentage ? (d.y1 === null || d.y0 === null ? null : d.y1 - d.y0) : d.initialY1; + }; + +const y0Accessor = + (stackMode?: StackMode) => + (d: DataSeriesDatum): number | null => { + // don't consider filled in data in the calculations + if (isDatumFilled(d)) { + return null; + } + return stackMode === StackMode.Percentage ? d.y0 : d.initialY0; + }; + /** @internal */ export function computeLegend( xDomain: XDomain, @@ -87,7 +109,7 @@ export function computeLegend( const legendItems: LegendItem[] = []; const defaultColor = theme.colors.defaultVizColor; - const legendValueMode = LegendValue.LastValue; + const legendValueMode = settingsSpec.legendValues[0] ?? LegendValue.None; dataSeries.forEach((series) => { const { specId, yAccessor } = series; @@ -118,16 +140,11 @@ export function computeLegend( const pointStyle = getPointStyle(spec, theme); - const itemValue = getLegendValue(series, xDomain, legendValueMode, (d) => { - return series.stackMode === StackMode.Percentage - ? d.y1 === null || d.y0 === null - ? null - : d.y1 - d.y0 - : d.initialY1; - }); + const itemValue = getLegendValue(series, xDomain, legendValueMode, y1Accessor(series.stackMode)); const formattedItemValue = itemValue !== null ? formatter(itemValue) : ''; legendItems.push({ + depth: 0, color, label: banded ? getBandedLegendItemLabel(name, BandedAccessorType.Y1, postFixes) : name, seriesIdentifiers: [seriesIdentifier], @@ -135,26 +152,26 @@ export function computeLegend( isSeriesHidden, isItemHidden: hideInLegend, isToggleable: true, - defaultExtra: + values: itemValue !== null - ? { - raw: itemValue, - formatted: formattedItemValue, - legendSizingLabel: formattedItemValue, - } - : undefined, + ? [ + { + value: itemValue, + label: formattedItemValue, + }, + ] + : [], path: [{ index: 0, value: seriesIdentifier.key }], keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()], pointStyle, }); if (banded) { - const bandedItemValue = getLegendValue(series, xDomain, legendValueMode, (d) => { - return series.stackMode === StackMode.Percentage ? d.y0 : d.initialY0; - }); + const bandedItemValue = getLegendValue(series, xDomain, legendValueMode, y0Accessor(series.stackMode)); const bandedFormattedItemValue = bandedItemValue !== null ? formatter(bandedItemValue) : ''; const labelY0 = getBandedLegendItemLabel(name, BandedAccessorType.Y0, postFixes); legendItems.push({ + depth: 0, color, label: labelY0, seriesIdentifiers: [seriesIdentifier], @@ -162,14 +179,15 @@ export function computeLegend( isSeriesHidden, isItemHidden: hideInLegend, isToggleable: true, - defaultExtra: + values: bandedItemValue !== null - ? { - raw: bandedItemValue, - formatted: bandedFormattedItemValue, - legendSizingLabel: bandedFormattedItemValue, - } - : undefined, + ? [ + { + value: bandedItemValue, + label: bandedFormattedItemValue, + }, + ] + : [], path: [{ index: 0, value: seriesIdentifier.key }], keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()], pointStyle, diff --git a/packages/charts/src/chart_types/xy_chart/rendering/rendering.test.ts b/packages/charts/src/chart_types/xy_chart/rendering/rendering.test.ts index c132e7f886..aea0204cd0 100644 --- a/packages/charts/src/chart_types/xy_chart/rendering/rendering.test.ts +++ b/packages/charts/src/chart_types/xy_chart/rendering/rendering.test.ts @@ -126,13 +126,10 @@ describe('Rendering utils', () => { const highlightedLegendItem: LegendItem = { color: '', label: '', + depth: 0, seriesIdentifiers: [seriesIdentifier], isSeriesHidden: false, - defaultExtra: { - formatted: null, - raw: null, - legendSizingLabel: null, - }, + values: [], path: [], keys: [], }; diff --git a/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_items_labels.ts b/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_items_labels.ts index f70c3384e1..dcaba2338a 100644 --- a/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_items_labels.ts +++ b/packages/charts/src/chart_types/xy_chart/state/selectors/get_legend_items_labels.ts @@ -6,20 +6,8 @@ * Side Public License, v 1. */ -import { computeLegendSelector } from './compute_legend'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { LegendItemLabel } from '../../../../state/selectors/get_legend_items_labels'; -import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_spec'; /** @internal */ -export const getLegendItemsLabelsSelector = createCustomCachedSelector( - [computeLegendSelector, getSettingsSpecSelector], - (legendItems, { showLegendExtra }): LegendItemLabel[] => - legendItems.map(({ label, defaultExtra }) => ({ - label: - defaultExtra && (defaultExtra.legendSizingLabel ?? null) !== null - ? `${label}${showLegendExtra ? defaultExtra.legendSizingLabel : ''}` - : label, - depth: 0, - })), -); +export const getLegendItemsLabelsSelector = createCustomCachedSelector([], (): LegendItemLabel[] => []); diff --git a/packages/charts/src/chart_types/xy_chart/state/utils/common.test.ts b/packages/charts/src/chart_types/xy_chart/state/utils/common.test.ts index a2ecbf2385..fe4ff09579 100644 --- a/packages/charts/src/chart_types/xy_chart/state/utils/common.test.ts +++ b/packages/charts/src/chart_types/xy_chart/state/utils/common.test.ts @@ -127,13 +127,14 @@ describe('Type Checks', () => { { color: '#1EA593', label: 'a', + depth: 0, seriesIdentifiers: [ { key: 'specId:{bars},colors:{a}', specId: 'bars', }, ], - defaultExtra: { raw: 6, formatted: '6.00', legendSizingLabel: '6.00' }, + values: [{ value: 6, label: '6.00' }], isSeriesHidden: true, path: [], keys: [], @@ -141,13 +142,14 @@ describe('Type Checks', () => { { color: '#2B70F7', label: 'b', + depth: 0, seriesIdentifiers: [ { key: 'specId:{bars},colors:{b}', specId: 'bars', }, ], - defaultExtra: { raw: 2, formatted: '2.00', legendSizingLabel: '2.00' }, + values: [{ value: 2, label: '2.00' }], isSeriesHidden: true, path: [], keys: [], @@ -160,13 +162,14 @@ describe('Type Checks', () => { { color: '#1EA593', label: 'a', + depth: 0, seriesIdentifiers: [ { key: 'specId:{bars},colors:{a}', specId: 'bars', }, ], - defaultExtra: { raw: 6, formatted: '6.00', legendSizingLabel: '6.00' }, + values: [{ value: 6, label: '6.00' }], isSeriesHidden: false, path: [], keys: [], @@ -174,13 +177,14 @@ describe('Type Checks', () => { { color: '#2B70F7', label: 'b', + depth: 0, seriesIdentifiers: [ { key: 'specId:{bars},colors:{b}', specId: 'bars', }, ], - defaultExtra: { raw: 2, formatted: '2.00', legendSizingLabel: '2.00' }, + values: [{ value: 2, label: '2.00' }], isSeriesHidden: true, path: [], keys: [], diff --git a/packages/charts/src/chart_types/xy_chart/state/utils/get_last_value.ts b/packages/charts/src/chart_types/xy_chart/state/utils/get_last_value.ts index b8e7db1b62..5a283ad987 100644 --- a/packages/charts/src/chart_types/xy_chart/state/utils/get_last_value.ts +++ b/packages/charts/src/chart_types/xy_chart/state/utils/get_last_value.ts @@ -6,22 +6,27 @@ * Side Public License, v 1. */ -import { $Values } from 'utility-types'; - +import { + firstNonNull, + lastNonNull, + median, + average, + min, + max, + sum, + countNonNull, + distinctCount, + variance, + stdDeviation, + range, + difference, + differencePercent, +} from '../../../../common/aggregations'; +import { LegendValue } from '../../../../common/legend'; import { ScaleType } from '../../../../scales/constants'; import { XDomain } from '../../domains/types'; -import { isDatumFilled } from '../../rendering/utils'; import { DataSeries, DataSeriesDatum } from '../../utils/series'; -/** @internal */ -export const LegendValue = Object.freeze({ - None: 'none' as const, - LastValue: 'lastValue' as const, - LastNonNullValue: 'lastNonNullValue' as const, -}); -/** @internal */ -export type LegendValue = $Values; - /** * This method return a value from a DataSeries that correspond to the type of value requested. * It in general compute the last, min, max, avg, sum of the value in a series. @@ -38,18 +43,42 @@ export function getLegendValue( if (xDomain.type === ScaleType.Ordinal) { return null; } - switch (type) { - case LegendValue.LastNonNullValue: { - const last = series.data.findLast((d) => d.x === xDomain.dataDomain[1] && valueAccessor(d) !== null); - return last ? valueAccessor(last) : null; - } + case LegendValue.FirstNonNullValue: + return firstNonNull(series.data, valueAccessor); + case LegendValue.LastNonNullValue: + return lastNonNull(series.data, valueAccessor); + case LegendValue.FirstValue: + const first = series.data.find((d) => d.x === xDomain.dataDomain[0]); + return first ? valueAccessor(first) : null; + case LegendValue.CurrentAndLastValue: // the current value will be passed directly to the legend component case LegendValue.LastValue: const last = series.data.findLast((d) => d.x === xDomain.dataDomain[1]); - if (last && !isDatumFilled(last)) { - return valueAccessor(last); - } - return null; + return last ? valueAccessor(last) : null; + case LegendValue.Average: + return average(series.data, valueAccessor); + case LegendValue.Median: + return median(series.data, valueAccessor); + case LegendValue.Min: + return min(series.data, valueAccessor); + case LegendValue.Max: + return max(series.data, valueAccessor); + case LegendValue.Total: + return sum(series.data, valueAccessor).sum; + case LegendValue.Count: + return countNonNull(series.data, valueAccessor); + case LegendValue.DistinctCount: + return distinctCount(series.data, valueAccessor); + case LegendValue.Variance: + return variance(series.data, valueAccessor); + case LegendValue.StdDeviation: + return stdDeviation(series.data, valueAccessor); + case LegendValue.Range: + return range(series.data, valueAccessor); + case LegendValue.Difference: + return difference(series.data, valueAccessor); + case LegendValue.DifferencePercent: + return differencePercent(series.data, valueAccessor); default: case LegendValue.None: return null; diff --git a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts index c079823e65..b837242c95 100644 --- a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts +++ b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts @@ -29,7 +29,7 @@ export function getLegendItemExtraValues(tooltipValues: TooltipValue[]): Map { const current: LegendItemExtraValues = seriesTooltipValues.get(seriesIdentifier.key) ?? new Map(); if (valueAccessor === BandedAccessorType.Y0 || valueAccessor === BandedAccessorType.Y1) { - current.set(valueAccessor, { formatted: formattedValue, raw: value }); + current.set(valueAccessor, { label: formattedValue, value }); } seriesTooltipValues.set(seriesIdentifier.key, current); }); diff --git a/packages/charts/src/common/aggregations.test.ts b/packages/charts/src/common/aggregations.test.ts new file mode 100644 index 0000000000..fcb0101d48 --- /dev/null +++ b/packages/charts/src/common/aggregations.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + countNonNull, + sum, + average, + median, + min, + max, + range, + distinctCount, + stdDeviation, + variance, + difference, + differencePercent, +} from './aggregations'; +const numericArray = [3, 4, 5, 6, 6, 7, 7.5, 8, 1, 2, 9]; +const numericAccessor = (d: number) => d; +const numericArrayWithNull = [3, null, 5, 6, null, 7, 7.5, 8, 1, 2, 9]; +const numericAccessorWithNull = (d: number | null) => d; +const objectArray = [ + { y: 3 }, + { y: 4 }, + { y: 5 }, + { y: 6 }, + { y: 6 }, + { y: 7 }, + { y: 7.5 }, + { y: 8 }, + { y: 1 }, + { y: 2 }, + { y: 9 }, +]; +const objArrayAccessor = (d: { y: number | null }) => d.y; + +const objectArrayWithNull = [ + { y: 3 }, + { y: null }, + { y: 5 }, + { y: 6 }, + { y: null }, + { y: 7 }, + { y: 7.5 }, + { y: 8 }, + { y: 1 }, + { y: 2 }, + { y: 9 }, +]; + +describe('Aggregations', () => { + it('sum', () => { + expect(sum(numericArray, numericAccessor).sum).toBe(58.5); + expect(sum(objectArray, objArrayAccessor).sum).toBe(58.5); + expect(sum(numericArrayWithNull, numericAccessorWithNull).sum).toBe(48.5); + expect(sum(objectArrayWithNull, objArrayAccessor).sum).toBe(48.5); + expect(sum(numericArrayWithNull, numericAccessorWithNull).validCount).toBe(9); + expect(sum(objectArrayWithNull, objArrayAccessor).validCount).toBe(9); + }); + it('count', () => { + expect(countNonNull(numericArray, numericAccessor)).toBe(11); + expect(countNonNull(objectArray, objArrayAccessor)).toBe(11); + expect(countNonNull(numericArrayWithNull, numericAccessorWithNull)).toBe(9); + expect(countNonNull(objectArrayWithNull, objArrayAccessor)).toBe(9); + }); + it('distinct count', () => { + expect(distinctCount(numericArray, numericAccessorWithNull)).toBe(10); + expect(distinctCount(objectArray, objArrayAccessor)).toBe(10); + expect(distinctCount(numericArrayWithNull, numericAccessorWithNull)).toBe(9); + expect(distinctCount(objectArrayWithNull, objArrayAccessor)).toBe(9); + }); + it('average', () => { + expect(average(numericArray, numericAccessorWithNull)).toBeCloseTo(5.318); + expect(average(objectArray, objArrayAccessor)).toBeCloseTo(5.318); + expect(average(numericArrayWithNull, numericAccessorWithNull)).toBeCloseTo(5.388); + expect(average(objectArrayWithNull, objArrayAccessor)).toBeCloseTo(5.388); + }); + it('median', () => { + expect(median(numericArray, numericAccessorWithNull)).toBe(6); + expect(median(objectArray, objArrayAccessor)).toBe(6); + expect(median(numericArrayWithNull, numericAccessorWithNull)).toBe(6); + expect(median(objectArrayWithNull, objArrayAccessor)).toBe(6); + }); + it('min', () => { + expect(min(numericArray, numericAccessorWithNull)).toBe(1); + expect(min(objectArray, objArrayAccessor)).toBe(1); + expect(min(numericArrayWithNull, numericAccessorWithNull)).toBe(1); + expect(min(objectArrayWithNull, objArrayAccessor)).toBe(1); + }); + it('max', () => { + expect(max(numericArray, numericAccessorWithNull)).toBe(9); + expect(max(objectArray, objArrayAccessor)).toBe(9); + expect(max(numericArrayWithNull, numericAccessorWithNull)).toBe(9); + expect(max(objectArrayWithNull, objArrayAccessor)).toBe(9); + }); + it('range', () => { + expect(range(numericArray, numericAccessorWithNull)).toBe(8); + expect(range(objectArray, objArrayAccessor)).toBe(8); + expect(range(numericArrayWithNull, numericAccessorWithNull)).toBe(8); + expect(range(objectArrayWithNull, objArrayAccessor)).toBe(8); + }); + it('diff', () => { + expect(difference(numericArray, numericAccessorWithNull)).toBe(6); + expect(difference(objectArray, objArrayAccessor)).toBe(6); + expect(difference(numericArrayWithNull, numericAccessorWithNull)).toBe(6); + expect(difference(objectArrayWithNull, objArrayAccessor)).toBe(6); + }); + it('diff percent', () => { + expect(differencePercent(numericArray, numericAccessorWithNull)).toBeCloseTo(0.666); + expect(differencePercent(objectArray, objArrayAccessor)).toBeCloseTo(0.666); + expect(differencePercent(numericArrayWithNull, numericAccessorWithNull)).toBeCloseTo(0.666); + expect(differencePercent(objectArrayWithNull, objArrayAccessor)).toBeCloseTo(0.666); + }); + it('variance', () => { + expect(variance(numericArray, numericAccessorWithNull)).toBeCloseTo(6.613); + expect(variance(objectArray, objArrayAccessor)).toBeCloseTo(6.613); + expect(variance(numericArrayWithNull, numericAccessorWithNull)).toBeCloseTo(7.986); + expect(variance(objectArrayWithNull, objArrayAccessor)).toBeCloseTo(7.986); + }); + it('std deviation', () => { + expect(stdDeviation(numericArray, numericAccessorWithNull)).toBeCloseTo(2.5716); + expect(stdDeviation(objectArray, objArrayAccessor)).toBeCloseTo(2.5716); + expect(stdDeviation(numericArrayWithNull, numericAccessorWithNull)).toBeCloseTo(2.8259); + expect(stdDeviation(objectArrayWithNull, objArrayAccessor)).toBeCloseTo(2.8259); + }); +}); diff --git a/packages/charts/src/common/aggregations.ts b/packages/charts/src/common/aggregations.ts new file mode 100644 index 0000000000..b704567af3 --- /dev/null +++ b/packages/charts/src/common/aggregations.ts @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @internal */ +export function sum(arr: D[], accessor: (d: D) => number | null): { sum: number; validCount: number } { + return arr.reduce<{ sum: number; validCount: number }>( + (acc, d) => { + const value = accessor(d); + return { + sum: acc.sum + (value !== null ? value : 0), + validCount: value !== null ? acc.validCount + 1 : acc.validCount, + }; + }, + { sum: 0, validCount: 0 }, + ); +} + +/** @internal */ +export function average(arr: D[], accessor: (d: D) => number | null): number { + const total = sum(arr, accessor); + if (total.validCount === 0) return NaN; + return total.sum / total.validCount; +} + +/** @internal */ +export function median(input: D[], accessor: (d: D) => number | null): number { + const arr: number[] = input.reduce((acc, d) => { + const value = accessor(d); + if (value !== null) { + acc.push(value); + } + return acc; + }, []); + if (!arr.length) return NaN; + const s = arr.slice().sort((a, b) => a - b); + const mid = Math.floor(s.length / 2); + if (s.length % 2) { + return s[mid] ?? NaN; + } + return ((s[mid - 1] ?? NaN) + (s[mid] ?? NaN)) / 2; +} + +/** @internal */ +export function min(arr: D[], accessor: (d: D) => number | null): number { + if (arr.length === 0) return NaN; + return arr.reduce((m, d) => { + const value = accessor(d); + return value !== null ? Math.min(m, value) : m; + }, +Infinity); +} +/** @internal */ +export function max(arr: D[], accessor: (d: D) => number | null): number { + if (arr.length === 0) return NaN; + return arr.reduce((m, d) => { + const value = accessor(d); + return value !== null ? Math.max(m, value) : m; + }, -Infinity); +} + +/** @internal */ +export function range(arr: D[], accessor: (d: D) => number | null): number { + if (arr.length === 0) return NaN; + const minMax = arr.reduce<{ min: number; max: number }>( + (m, d) => { + const value = accessor(d); + return { + max: value !== null ? Math.max(m.max, value) : m.max, + min: value !== null ? Math.min(m.min, value) : m.min, + }; + }, + { min: +Infinity, max: -Infinity }, + ); + return minMax.max - minMax.min; +} + +/** @internal */ +export function countNonNull(arr: D[], accessor: (d: D) => number | null): number { + return arr.filter((d) => accessor(d) !== null).length; +} +/** @internal */ +export function distinctCount(arr: D[], accessor: (d: D) => number | null): number { + return new Set(nonNullArray(arr, accessor)).size; +} + +/** @internal */ +export function nonNullArray(arr: D[], accessor: (d: D) => number | null): number[] { + return arr.reduce((acc, d) => { + const value = accessor(d); + if (value !== null) { + acc.push(value); + } + return acc; + }, []); +} + +/** @internal */ +export function variance(arr: D[], accessor: (d: D) => number | null): number { + const nonNullArr = nonNullArray(arr, accessor); + const ddof = 1; + const total = sum(nonNullArr, (d) => d); + const avg = total.sum / total.validCount; + const squareDiffs = nonNullArr.map((d) => { + return Math.abs(Math.pow(d - avg, 2)); + }); + const totalSumOfSquareDiffs = sum(squareDiffs, (d: number) => d); + return totalSumOfSquareDiffs.sum / (totalSumOfSquareDiffs.validCount - ddof); +} + +/** @internal */ +export function stdDeviation(arr: D[], accessor: (d: D) => number | null): number { + const v = variance(arr, accessor); + const std = Math.sqrt(v); + return std; +} + +/** @internal */ +export function firstNonNull(arr: D[], accessor: (d: D) => number | null): number | null { + for (let i = 0; i < arr.length; i++) { + const item = arr[i]; + if (!item) continue; + const value = accessor(item); + if (value) { + return value; + } + } + return null; +} + +/** @internal */ +export function lastNonNull(arr: D[], accessor: (d: D) => number | null): number | null { + for (let i = arr.length - 1; i >= 0; i--) { + const item = arr[i]; + if (!item) continue; + const value = accessor(item); + if (value) { + return value; + } + } + return null; +} + +/** @internal */ +export function difference(arr: D[], accessor: (d: D) => number | null): number | null { + const first = firstNonNull(arr, accessor); + const last = lastNonNull(arr, accessor); + if (first !== null && last !== null) { + return last - first; + } + return null; +} + +/** @internal */ +export function differencePercent(arr: D[], accessor: (d: D) => number | null): number | null { + const first = firstNonNull(arr, accessor); + const last = lastNonNull(arr, accessor); + if (first !== null && last !== null) { + return (last - first) / last; + } + return null; +} diff --git a/packages/charts/src/common/legend.ts b/packages/charts/src/common/legend.ts index 91308df8e8..dcc9ab0360 100644 --- a/packages/charts/src/common/legend.ts +++ b/packages/charts/src/common/legend.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import { $Values } from 'utility-types'; + import { CategoryKey, CategoryLabel } from './category'; import { Color } from './colors'; import { SeriesIdentifier } from './series_id'; @@ -17,11 +19,60 @@ import { PointStyle } from '../utils/themes/theme'; /** @internal */ export type LegendItemChildId = CategoryKey; +/** @public */ +export type LegendItemValue = { value: PrimitiveValue; label: string }; + +/** @public */ +export const LegendValue = Object.freeze({ + None: 'none' as const, + /** Value of the bucket being hovered or last bucket value when not hovering. */ + CurrentAndLastValue: 'currentAndLastValue' as const, + /** Last value considering all data points in the chart */ + LastValue: 'lastValue' as const, + /** Last non-null value */ + LastNonNullValue: 'lastNonNullValue' as const, + /** Average value considering all data points in the chart */ + Average: 'average' as const, + /** Median value considering all data points in the chart */ + Median: 'median' as const, + /** Maximum value considering all data points in the chart */ + Max: 'max' as const, + /** Minimum value considering all data points in the chart */ + Min: 'min' as const, + /** First value considering all data points in the chart */ + FirstValue: 'firstValue' as const, + /** First non-null value */ + FirstNonNullValue: 'firstNonNullValue' as const, + /** Sum of al values plotted in the chart */ + Total: 'total' as const, + /** number of data points plotted in the chart */ + Count: 'count' as const, + /** number of data points with different values plotted in the chart */ + DistinctCount: 'distinctCount' as const, + /** Variance of all data points plotted in the chart */ + Variance: 'variance' as const, + /** Standard deviation of all data points plotted in the chart */ + StdDeviation: 'stdDeviation' as const, + /** Difference between min and max values */ + Range: 'range' as const, + /** Difference between first and last values */ + Difference: 'difference' as const, + /** % difference between first and last values */ + DifferencePercent: 'differencePercent' as const, + /** Partition section value */ + Value: 'value' as const, + /** Partition section value in percent */ + Percent: 'percent' as const, +}); +/** @public */ +export type LegendValue = $Values; + /** @internal */ export type LegendItem = { seriesIdentifiers: SeriesIdentifier[]; childId?: LegendItemChildId; - depth?: number; + // zero indexed + depth: number; /** * Path to iterm in hierarchical legend */ @@ -30,11 +81,7 @@ export type LegendItem = { label: CategoryLabel; isSeriesHidden?: boolean; isItemHidden?: boolean; - defaultExtra?: { - raw: number | null; - formatted: number | string | null; - legendSizingLabel: number | string | null; - }; + values: Array; // TODO: Remove when partition layers are toggleable isToggleable?: boolean; keys: Array; @@ -43,4 +90,4 @@ export type LegendItem = { }; /** @internal */ -export type LegendItemExtraValues = Map; +export type LegendItemExtraValues = Map; diff --git a/packages/charts/src/components/__snapshots__/chart.test.tsx.snap b/packages/charts/src/components/__snapshots__/chart.test.tsx.snap index 060963eb45..3695910d87 100644 --- a/packages/charts/src/components/__snapshots__/chart.test.tsx.snap +++ b/packages/charts/src/components/__snapshots__/chart.test.tsx.snap @@ -26,7 +26,7 @@ exports[`Chart should render the legend name test 1`] = `
    - +
  • diff --git a/packages/charts/src/components/legend/__snapshots__/legend.test.tsx.snap b/packages/charts/src/components/legend/__snapshots__/legend.test.tsx.snap index 4be56c20f0..1d65208fad 100644 --- a/packages/charts/src/components/legend/__snapshots__/legend.test.tsx.snap +++ b/packages/charts/src/components/legend/__snapshots__/legend.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Legend #legendColorPicker should match snapshot after onChange is called 1`] = ` -" +"
  • @@ -23,7 +23,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
  • -
    +
  • @@ -45,7 +45,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
  • -
    +
  • @@ -67,7 +67,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
  • -
    +
  • @@ -93,7 +93,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle `; exports[`Legend #legendColorPicker should match snapshot after onClose is called 1`] = ` -" +"
  • @@ -115,7 +115,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
  • -
    +
  • @@ -137,7 +137,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
  • -
    +
  • @@ -159,7 +159,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
  • -
    +
  • @@ -199,7 +199,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click `; exports[`Legend #legendColorPicker should render colorPicker when color is clicked 2`] = ` -" +"
  • @@ -234,7 +234,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
    - +
  • @@ -256,7 +256,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
  • -
    +
  • @@ -278,7 +278,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
  • -
    +
  • diff --git a/packages/charts/src/components/legend/extra.tsx b/packages/charts/src/components/legend/extra.tsx deleted file mode 100644 index cad6e492b5..0000000000 --- a/packages/charts/src/components/legend/extra.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; - -/** - * @internal - * @param extra - * @param isSeriesHidden - */ -export function renderExtra(extra: string) { - return ( -
    - {extra} -
    - ); -} diff --git a/packages/charts/src/components/legend/legend.test.tsx b/packages/charts/src/components/legend/legend.test.tsx index 0094df800b..b60e4a8e91 100644 --- a/packages/charts/src/components/legend/legend.test.tsx +++ b/packages/charts/src/components/legend/legend.test.tsx @@ -11,6 +11,7 @@ import React, { Component } from 'react'; import { Legend } from './legend'; import { LegendListItem } from './legend_item'; +import { LegendValue } from '../../common/legend'; import { SeededDataGenerator } from '../../mocks/utils'; import { ScaleType } from '../../scales/constants'; import { Settings, BarSeries, LegendColorPicker } from '../../specs'; @@ -22,7 +23,7 @@ describe('Legend', () => { it('shall render the all the series names', () => { const wrapper = mount( - + { it('shall render the all the series names without the data value', () => { const wrapper = mount( - + { const data = dg.generateGroupedSeries(10, numberOfSeries, 'split'); const wrapper = mount( - + { const data = dg.generateGroupedSeries(10, numberOfSeries, 'split'); const wrapper = mount( - + { const data = [{ x: 2, y: 5 }]; const wrapper = mount( - + { it('should test default dot icons', () => { const wrapper = mount( - + { it('should align styles - stroke', () => { const wrapperColorChange = mount( - + ; - showExtra: boolean; + legendValues: Array; isMostlyRTL: boolean; labelOptions: LegendLabelOptions; colorPicker?: LegendColorPicker; @@ -172,7 +171,7 @@ export class LegendListItem extends Component const { extraValues, item, - showExtra, + legendValues, colorPicker, totalItems, action: Action, @@ -190,7 +189,15 @@ export class LegendListItem extends Component 'echLegendItem--vertical': positionConfig.direction === LayoutDirection.Vertical, }); const hasColorPicker = Boolean(colorPicker); - const extra = showExtra ? getExtra(extraValues, item, totalItems) : null; + + // only the first for now until https://github.com/elastic/elastic-charts/issues/2096 + const legendValue = + legendValues[0] === LegendValue.CurrentAndLastValue + ? getExtra(extraValues, item, totalItems) + : legendValues.length > 0 + ? item.values[0] + : undefined; + const style: CSSProperties = flatLegend ? {} : { @@ -225,7 +232,11 @@ export class LegendListItem extends Component onToggle={this.onLabelToggle(seriesIdentifiers)} isSeriesHidden={isSeriesHidden} /> - {extra && !isSeriesHidden && renderExtra(extra.formatted)} + {legendValue && !isSeriesHidden && ( +
    + {legendValue.label} +
    + )} {Action && (
    diff --git a/packages/charts/src/components/legend/utils.ts b/packages/charts/src/components/legend/utils.ts index b7575c1d98..47b7abebe2 100644 --- a/packages/charts/src/components/legend/utils.ts +++ b/packages/charts/src/components/legend/utils.ts @@ -6,19 +6,18 @@ * Side Public License, v 1. */ -import { PrimitiveValue } from '../../chart_types/partition_chart/layout/utils/group_by_rollup'; -import { LegendItemExtraValues, LegendItem } from '../../common/legend'; +import { LegendItemExtraValues, LegendItem, LegendItemValue } from '../../common/legend'; /** @internal */ export function getExtra( extraValues: Map, item: LegendItem, totalItems: number, -): { raw: PrimitiveValue; formatted: string } | null { - const { seriesIdentifiers, defaultExtra, childId, path } = item; +): LegendItemValue | undefined { + const { seriesIdentifiers, values, childId, path } = item; // don't show extra if the legend item is associated with multiple series if (extraValues.size === 0 || seriesIdentifiers.length > 1 || !seriesIdentifiers[0]) { - return defaultExtra ? { formatted: `${defaultExtra.formatted ?? ''}`, raw: defaultExtra.raw } : null; + return values.length > 0 ? { label: `${values[0]?.label ?? ''}`, value: values[0]?.value ?? null } : undefined; } const [{ key }] = seriesIdentifiers; const extraValueKey = path.map(({ index }) => index).join('__'); @@ -26,7 +25,7 @@ export function getExtra( const actionExtra = childId !== undefined ? itemExtraValues?.get(childId) : undefined; return actionExtra ? actionExtra - : extraValues.size === totalItems && defaultExtra - ? { formatted: `${defaultExtra.formatted ?? ''}`, raw: defaultExtra.raw } - : null; + : extraValues.size === totalItems && values.length > 0 + ? { label: `${values[0]?.label ?? ''}`, value: values[0]?.value ?? null } + : undefined; } diff --git a/packages/charts/src/index.ts b/packages/charts/src/index.ts index 6e27f1589b..62986887b1 100644 --- a/packages/charts/src/index.ts +++ b/packages/charts/src/index.ts @@ -46,6 +46,7 @@ export { } from './chart_types/xy_chart/annotations/types'; export { GeometryValue, BandedAccessorType } from './utils/geometry'; export { LegendPath, LegendPathElement } from './state/actions/legend'; +export { LegendItemValue, LegendValue } from './common/legend'; export { CategoryKey, CategoryLabel } from './common/category'; export { Layer as PartitionLayer, PartitionProps } from './chart_types/partition_chart/specs/index'; export { FillLabelConfig as PartitionFillLabel, PartitionStyle } from './utils/themes/partition'; diff --git a/packages/charts/src/specs/constants.ts b/packages/charts/src/specs/constants.ts index 64ba2ba794..b9679772ae 100644 --- a/packages/charts/src/specs/constants.ts +++ b/packages/charts/src/specs/constants.ts @@ -134,7 +134,7 @@ export type TooltipStickTo = $Values; export const DEFAULT_LEGEND_CONFIG = { showLegend: false, legendSize: NaN, - showLegendExtra: false, + legendValues: [], legendMaxDepth: Infinity, legendPosition: Position.Right, flatLegend: false, diff --git a/packages/charts/src/specs/settings.test.tsx b/packages/charts/src/specs/settings.test.tsx index 911b7ed604..fc71586f03 100644 --- a/packages/charts/src/specs/settings.test.tsx +++ b/packages/charts/src/specs/settings.test.tsx @@ -83,7 +83,7 @@ describe('Settings spec component', () => { expect(settingSpec.animateData).toBe(true); expect(settingSpec.showLegend).toEqual(true); expect(settingSpec.legendPosition).toBe(Position.Bottom); - expect(settingSpec.showLegendExtra).toEqual(false); + expect(settingSpec.legendValues).toEqual([]); expect(settingSpec.debug).toBe(true); expect(settingSpec.xDomain).toEqual({ min: 0, max: 10 }); }); @@ -161,7 +161,7 @@ describe('Settings spec component', () => { animateData: true, showLegend: true, legendPosition: Position.Bottom, - showLegendExtra: false, + legendValues: [], debug: true, xDomain: { min: 0, max: 10 }, }; diff --git a/packages/charts/src/specs/settings.tsx b/packages/charts/src/specs/settings.tsx index f5ab07b7a0..b0ee71b1ec 100644 --- a/packages/charts/src/specs/settings.tsx +++ b/packages/charts/src/specs/settings.tsx @@ -18,6 +18,7 @@ import { WordModel } from '../chart_types/wordcloud/layout/types/viewmodel_types import { XYChartSeriesIdentifier } from '../chart_types/xy_chart/utils/series'; import { CategoryLabel } from '../common/category'; import { Color } from '../common/colors'; +import { LegendItemValue, LegendValue } from '../common/legend'; import { SmallMultiplesDatum } from '../common/panel_utils'; import { SeriesIdentifier } from '../common/series_id'; import { TooltipPortalSettings } from '../components'; @@ -390,7 +391,7 @@ export interface CustomLegendProps { label: CategoryLabel; seriesType?: SeriesType; pointStyle?: PointStyle; - extraValue?: { raw: PrimitiveValue; formatted: string }; + extraValue?: LegendItemValue; isSeriesHidden?: boolean; onItemOverActon: () => void; onItemOutAction: () => void; @@ -420,10 +421,10 @@ export interface LegendSpec { */ legendPosition: Position | LegendPositionConfig; /** - * Show an extra parameter on each legend item defined by the chart type - * @defaultValue `false` + * Add one or more computed statistics to each legend item. + * The available statistics depends by chart type. */ - showLegendExtra: boolean; + legendValues: Array; /** * Limit the legend to the specified maximal depth when showing a hierarchical legend * diff --git a/packages/charts/src/state/selectors/get_legend_config_selector.ts b/packages/charts/src/state/selectors/get_legend_config_selector.ts index 9606e8fa18..0c1565f413 100644 --- a/packages/charts/src/state/selectors/get_legend_config_selector.ts +++ b/packages/charts/src/state/selectors/get_legend_config_selector.ts @@ -28,7 +28,7 @@ export const getLegendConfigSelector = createCustomCachedSelector( onLegendItemOut, onLegendItemOver, onLegendItemPlusClick, - showLegendExtra, + legendValues, }) => { return { flatLegend, @@ -45,7 +45,7 @@ export const getLegendConfigSelector = createCustomCachedSelector( onLegendItemOut, onLegendItemOver, onLegendItemPlusClick, - showLegendExtra, + legendValues, }; }, ); diff --git a/packages/charts/src/state/selectors/get_legend_size.ts b/packages/charts/src/state/selectors/get_legend_size.ts index 8ab81b5653..3ea9486025 100644 --- a/packages/charts/src/state/selectors/get_legend_size.ts +++ b/packages/charts/src/state/selectors/get_legend_size.ts @@ -8,7 +8,7 @@ import { getChartThemeSelector } from './get_chart_theme'; import { getLegendConfigSelector } from './get_legend_config_selector'; -import { getLegendItemsLabelsSelector } from './get_legend_items_labels'; +import { getLegendItemsSelector } from './get_legend_items'; import { DEFAULT_FONT_FAMILY } from '../../common/default_theme_attributes'; import { LEGEND_HIERARCHY_MARGIN } from '../../components/legend/legend_item'; import { LEGEND_TO_FULL_CONFIG } from '../../components/legend/position_style'; @@ -35,17 +35,23 @@ export type LegendSizing = Size & { /** @internal */ export const getLegendSizeSelector = createCustomCachedSelector( - [getLegendConfigSelector, getChartThemeSelector, getParentDimensionSelector, getLegendItemsLabelsSelector], - (legendConfig, theme, parentDimensions, labels): LegendSizing => { - if (!legendConfig.showLegend) { + [getLegendConfigSelector, getChartThemeSelector, getParentDimensionSelector, getLegendItemsSelector], + ( + { showLegend, legendSize, legendValues, legendPosition, legendAction }, + theme, + parentDimensions, + items, + ): LegendSizing => { + if (!showLegend) { return { width: 0, height: 0, margin: 0, position: LEGEND_TO_FULL_CONFIG[Position.Right] }; } const bbox = withTextMeasure((textMeasure) => - labels.reduce( - (acc, { label, depth }) => { + items.reduce( + (acc, { label, depth, values }) => { + const itemLabel = `${label}${legendValues.length > 0 ? values[0]?.label ?? '' : ''}`; const { width, height } = textMeasure( - label, + itemLabel, { fontFamily: DEFAULT_FONT_FAMILY, fontVariant: 'normal', fontWeight: 400, fontStyle: 'normal' }, 12, 1.5, @@ -58,22 +64,22 @@ export const getLegendSizeSelector = createCustomCachedSelector( ), ); - const { showLegendExtra: showLegendDisplayValue, legendPosition, legendAction } = legendConfig; const { legend: { verticalWidth, spacingBuffer, margin }, } = theme; const actionDimension = isDefined(legendAction) ? 24 : 0; // max width plus margin - const legendItemWidth = MARKER_WIDTH + SHARED_MARGIN + bbox.width + (showLegendDisplayValue ? SHARED_MARGIN : 0); + const showExtraMargin = legendValues.length > 0; // && items.every(({ values }) => values.length > 0); // remove unnecessary margin + const legendItemWidth = MARKER_WIDTH + SHARED_MARGIN + bbox.width + (showExtraMargin ? SHARED_MARGIN : 0); if (legendPosition.direction === LayoutDirection.Vertical) { const legendItemHeight = bbox.height + VERTICAL_PADDING * 2; - const legendHeight = legendItemHeight * labels.length + TOP_MARGIN; + const legendHeight = legendItemHeight * items.length + TOP_MARGIN; const scrollBarDimension = legendHeight > parentDimensions.height ? SCROLL_BAR_WIDTH : 0; const staticWidth = spacingBuffer + actionDimension + scrollBarDimension; - const width = Number.isFinite(legendConfig.legendSize) - ? Math.min(Math.max(legendConfig.legendSize, legendItemWidth * 0.3 + staticWidth), parentDimensions.width * 0.7) + const width = Number.isFinite(legendSize) + ? Math.min(Math.max(legendSize, legendItemWidth * 0.3 + staticWidth), parentDimensions.width * 0.7) : Math.floor(Math.min(legendItemWidth + staticWidth, verticalWidth)); return { @@ -83,9 +89,9 @@ export const getLegendSizeSelector = createCustomCachedSelector( position: legendPosition, }; } - const isSingleLine = (parentDimensions.width - 20) / 200 > labels.length; - const height = Number.isFinite(legendConfig.legendSize) - ? Math.min(legendConfig.legendSize, parentDimensions.height * 0.7) + const isSingleLine = (parentDimensions.width - 20) / 200 > items.length; + const height = Number.isFinite(legendSize) + ? Math.min(legendSize, parentDimensions.height * 0.7) : isSingleLine ? bbox.height + 16 : bbox.height * 2 + 24; diff --git a/storybook/stories/annotations/lines/1_x_continuous.story.tsx b/storybook/stories/annotations/lines/1_x_continuous.story.tsx index 7ea6829487..2f81ecec24 100644 --- a/storybook/stories/annotations/lines/1_x_continuous.story.tsx +++ b/storybook/stories/annotations/lines/1_x_continuous.story.tsx @@ -19,6 +19,7 @@ import { LineAnnotationStyle, ScaleType, Settings, + LegendValue, } from '@elastic/charts'; import { Icon } from '@elastic/charts/src/components/icons/icon'; import { Position } from '@elastic/charts/src/utils/common'; @@ -55,7 +56,13 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + ( - + { const y1AccessorFormat = text('y1AccessorFormat', ''); return ( - + ( - + { const allMetrics = [...data3, ...data2, ...data1]; return ( - + { const stackedAsPercentage = boolean('stacked as percentage', true); return ( - + ( - + ( - + { - const showLegend = boolean('Show legend', true, 'Y axis'); + const showLegend = boolean('Show legend', true); + const showLegendExtra = boolean('Show legend values', true); const disableYAxisFormat = boolean('Disable Axis tickFormat', false, 'Y axis'); const yAxisFormat = text('Axis value format', '0[.]0', 'Y axis'); const yAxisUnit = text('Axis unit', 'pets', 'Y axis'); @@ -31,7 +32,11 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + `${value}${headerUnit ? ` ${headerUnit}` : ''}` diff --git a/storybook/stories/bar/10_axis_and_legend.story.tsx b/storybook/stories/bar/10_axis_and_legend.story.tsx index ecf3b91a27..d454dda012 100644 --- a/storybook/stories/bar/10_axis_and_legend.story.tsx +++ b/storybook/stories/bar/10_axis_and_legend.story.tsx @@ -8,14 +8,19 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/11_stacked_with_axis_and_legend.story.tsx b/storybook/stories/bar/11_stacked_with_axis_and_legend.story.tsx index 5111049ed5..802f0f0bd9 100644 --- a/storybook/stories/bar/11_stacked_with_axis_and_legend.story.tsx +++ b/storybook/stories/bar/11_stacked_with_axis_and_legend.story.tsx @@ -8,14 +8,19 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/13_clustered.story.tsx b/storybook/stories/bar/13_clustered.story.tsx index f2c6b722d6..5d6b78b314 100644 --- a/storybook/stories/bar/13_clustered.story.tsx +++ b/storybook/stories/bar/13_clustered.story.tsx @@ -9,7 +9,7 @@ import { number } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, PartialTheme, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -36,7 +36,7 @@ export const Example: ChartsStory = (_, { title, description }) => { ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/18_bar_chart_1y0g.story.tsx b/storybook/stories/bar/18_bar_chart_1y0g.story.tsx index 7960a3c11d..7cbb19a408 100644 --- a/storybook/stories/bar/18_bar_chart_1y0g.story.tsx +++ b/storybook/stories/bar/18_bar_chart_1y0g.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -16,7 +16,12 @@ import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/19_bar_chart_1y1g.story.tsx b/storybook/stories/bar/19_bar_chart_1y1g.story.tsx index 55e74a7658..cf25074a6d 100644 --- a/storybook/stories/bar/19_bar_chart_1y1g.story.tsx +++ b/storybook/stories/bar/19_bar_chart_1y1g.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -16,7 +16,12 @@ import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/20_bar_chart_1y2g.story.tsx b/storybook/stories/bar/20_bar_chart_1y2g.story.tsx index 180d794224..1182910302 100644 --- a/storybook/stories/bar/20_bar_chart_1y2g.story.tsx +++ b/storybook/stories/bar/20_bar_chart_1y2g.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -16,7 +16,12 @@ import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/21_bar_chart_2y0g.story.tsx b/storybook/stories/bar/21_bar_chart_2y0g.story.tsx index 6cd5f06cad..25bae33404 100644 --- a/storybook/stories/bar/21_bar_chart_2y0g.story.tsx +++ b/storybook/stories/bar/21_bar_chart_2y0g.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -16,7 +16,12 @@ import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/22_barchart_2y1g.story.tsx b/storybook/stories/bar/22_barchart_2y1g.story.tsx index 7a2a81a3bf..a289e4c825 100644 --- a/storybook/stories/bar/22_barchart_2y1g.story.tsx +++ b/storybook/stories/bar/22_barchart_2y1g.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -16,7 +16,12 @@ import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/23_bar_chart_2y2g.story.tsx b/storybook/stories/bar/23_bar_chart_2y2g.story.tsx index 5e3a561eaa..9d144c18b5 100644 --- a/storybook/stories/bar/23_bar_chart_2y2g.story.tsx +++ b/storybook/stories/bar/23_bar_chart_2y2g.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -16,7 +16,12 @@ import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> { const isVisibleFunction: FilterPredicate = (series) => series.splitAccessors.get('g1') === 'cloudflare.com'; return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/bar/2_label_value.story.tsx b/storybook/stories/bar/2_label_value.story.tsx index 62217d1957..3e2fece1c4 100644 --- a/storybook/stories/bar/2_label_value.story.tsx +++ b/storybook/stories/bar/2_label_value.story.tsx @@ -19,6 +19,7 @@ import { ScaleType, Settings, PartialTheme, + LegendValue, } from '@elastic/charts'; import { SeededDataGenerator } from '@elastic/charts/src/mocks/utils'; @@ -99,7 +100,7 @@ export const Example: ChartsStory = (_, { title, description }) => { debug={debug} rotation={customKnobs.enum.rotation()} showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} legendPosition={customKnobs.enum.position('legend')} /> diff --git a/storybook/stories/bar/50_order_bins_by_sum.story.tsx b/storybook/stories/bar/50_order_bins_by_sum.story.tsx index d324b2c3a9..b62624a05c 100644 --- a/storybook/stories/bar/50_order_bins_by_sum.story.tsx +++ b/storybook/stories/bar/50_order_bins_by_sum.story.tsx @@ -9,7 +9,7 @@ import { select, boolean } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings, BinAgg, Direction } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, BinAgg, Direction, LegendValue } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -69,7 +69,7 @@ export const Example: ChartsStory = (_, { title, description }) => { : undefined } showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} baseTheme={useBaseTheme()} legendPosition={Position.Right} /> diff --git a/storybook/stories/bar/51_label_value_advanced.story.tsx b/storybook/stories/bar/51_label_value_advanced.story.tsx index fbb295d101..2f93db918f 100644 --- a/storybook/stories/bar/51_label_value_advanced.story.tsx +++ b/storybook/stories/bar/51_label_value_advanced.story.tsx @@ -15,6 +15,7 @@ import { Chart, DisplayValueSpec, LabelOverflowConstraint, + LegendValue, PartialTheme, Position, ScaleType, @@ -130,7 +131,7 @@ export const Example: ChartsStory = (_, { title, description }) => { debug={debug} rotation={customKnobs.enum.rotation()} showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} /> Number(d).toFixed(2)} /> diff --git a/storybook/stories/debug/1_basic.story.tsx b/storybook/stories/debug/1_basic.story.tsx index 178fc410cd..4973504495 100644 --- a/storybook/stories/debug/1_basic.story.tsx +++ b/storybook/stories/debug/1_basic.story.tsx @@ -9,7 +9,17 @@ import { boolean } from '@storybook/addon-knobs'; import React from 'react'; -import { Chart, ScaleType, Settings, Position, Axis, BarSeries, AreaSeries, CurveType } from '@elastic/charts'; +import { + Chart, + ScaleType, + Settings, + Position, + Axis, + BarSeries, + AreaSeries, + CurveType, + LegendValue, +} from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -19,7 +29,13 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/debug/2_debug_state.story.tsx b/storybook/stories/debug/2_debug_state.story.tsx index 453933e742..d15156838f 100644 --- a/storybook/stories/debug/2_debug_state.story.tsx +++ b/storybook/stories/debug/2_debug_state.story.tsx @@ -20,6 +20,7 @@ import { Axis, Position, SeriesNameFn, + LegendValue, } from '@elastic/charts'; import { SeededDataGenerator } from '@elastic/charts/src/mocks/utils'; @@ -50,7 +51,7 @@ export const Example: ChartsStory = (_, { title, description }) => { debug={debug} debugState={debugState} showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} baseTheme={useBaseTheme()} /> diff --git a/storybook/stories/interactions/15_render_change.story.tsx b/storybook/stories/interactions/15_render_change.story.tsx index 3a232612c0..23d11120b3 100644 --- a/storybook/stories/interactions/15_render_change.story.tsx +++ b/storybook/stories/interactions/15_render_change.story.tsx @@ -9,7 +9,7 @@ import { action } from '@storybook/addon-actions'; import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -20,7 +20,7 @@ export const Example: ChartsStory = (_, { title, description }) => ( { { { {selectedChart === ChartType.Partition ? renderPartitionChart() diff --git a/storybook/stories/interactions/1_bar_clicks.story.tsx b/storybook/stories/interactions/1_bar_clicks.story.tsx index 06a03376dd..58a175dfda 100644 --- a/storybook/stories/interactions/1_bar_clicks.story.tsx +++ b/storybook/stories/interactions/1_bar_clicks.story.tsx @@ -14,6 +14,7 @@ import { Axis, BarSeries, Chart, + LegendValue, Position, ScaleType, Settings, @@ -50,7 +51,7 @@ export const Example: ChartsStory = (_, { title, description }) => { ( ( ( { { ( ( ( { { showLegend theme={{ legend: { labelOptions } }} baseTheme={useBaseTheme()} - showLegendExtra={showLegendExtra} + legendValues={showLegendExtra ? [LegendValue.CurrentAndLastValue] : []} legendPosition={legendPosition} legendAction={hideActions ? undefined : getLegendAction(euiPopoverPosition)} legendColorPicker={showColorPicker ? getColorPicker(euiPopoverPosition) : undefined} diff --git a/storybook/stories/legend/13_inside_chart.story.tsx b/storybook/stories/legend/13_inside_chart.story.tsx index fc095caf2d..f561bba093 100644 --- a/storybook/stories/legend/13_inside_chart.story.tsx +++ b/storybook/stories/legend/13_inside_chart.story.tsx @@ -20,6 +20,7 @@ import { VerticalAlignment, HorizontalAlignment, LayoutDirection, + LegendValue, } from '@elastic/charts'; import { SeededDataGenerator } from '@elastic/charts/src/mocks/utils'; import { KIBANA_METRICS } from '@elastic/charts/src/utils/data_samples/test_dataset_kibana'; @@ -84,7 +85,7 @@ export const Example: ChartsStory = (_, { title, description }) => { { { onClick={() => i.onItemClickAction(false)} style={{ display: 'block', color: i.isSeriesHidden ? 'gray' : i.color }} > - {i.label} {i.extraValue?.formatted} + {i.label} {i.extraValue?.label} ))}
    @@ -62,7 +63,7 @@ export const Example: ChartsStory = (_, { title, description }) => { { { const splitSeries = boolean('split series', true) ? ['g1', 'g2'] : undefined; return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/legend/6_hide_legend.story.tsx b/storybook/stories/legend/6_hide_legend.story.tsx index 2ab8d566e9..cfb721a943 100644 --- a/storybook/stories/legend/6_hide_legend.story.tsx +++ b/storybook/stories/legend/6_hide_legend.story.tsx @@ -9,7 +9,7 @@ import { boolean } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -20,7 +20,12 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/legend/8_spacing_buffer.story.tsx b/storybook/stories/legend/8_spacing_buffer.story.tsx index 07a1df87e3..b923c54189 100644 --- a/storybook/stories/legend/8_spacing_buffer.story.tsx +++ b/storybook/stories/legend/8_spacing_buffer.story.tsx @@ -9,7 +9,7 @@ import { boolean, number } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings, PartialTheme } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, PartialTheme, LegendValue } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -27,7 +27,13 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/line/11_discontinuous_data_points.story.tsx b/storybook/stories/line/11_discontinuous_data_points.story.tsx index f6e2b45209..6745614943 100644 --- a/storybook/stories/line/11_discontinuous_data_points.story.tsx +++ b/storybook/stories/line/11_discontinuous_data_points.story.tsx @@ -9,7 +9,18 @@ import { boolean } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, Chart, CurveType, LineSeries, Position, ScaleType, Settings, Fit, AreaSeries } from '@elastic/charts'; +import { + Axis, + Chart, + CurveType, + LineSeries, + Position, + ScaleType, + Settings, + Fit, + AreaSeries, + LegendValue, +} from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -20,7 +31,12 @@ export const Example: ChartsStory = (_, { title, description }) => { const LineOrAreaSeries = isArea ? AreaSeries : LineSeries; return ( - + diff --git a/storybook/stories/line/14_point_shapes.story.tsx b/storybook/stories/line/14_point_shapes.story.tsx index c38543014b..41f8bb8f60 100644 --- a/storybook/stories/line/14_point_shapes.story.tsx +++ b/storybook/stories/line/14_point_shapes.story.tsx @@ -12,6 +12,7 @@ import React from 'react'; import { Axis, Chart, + LegendValue, LIGHT_THEME, LineSeries, niceTimeFormatByDay, @@ -38,7 +39,7 @@ export const Example: ChartsStory = (_, { title, description }) => { ( - + ( - + ( - + ( - + ( - + ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/mixed/2_lines_and_areas.story.tsx b/storybook/stories/mixed/2_lines_and_areas.story.tsx index 14392e8e7a..81cb03bf4a 100644 --- a/storybook/stories/mixed/2_lines_and_areas.story.tsx +++ b/storybook/stories/mixed/2_lines_and_areas.story.tsx @@ -8,14 +8,19 @@ import React from 'react'; -import { AreaSeries, Axis, Chart, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; +import { AreaSeries, Axis, Chart, LegendValue, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/mixed/3_areas_and_bars.story.tsx b/storybook/stories/mixed/3_areas_and_bars.story.tsx index 90b1f152f5..64b43db057 100644 --- a/storybook/stories/mixed/3_areas_and_bars.story.tsx +++ b/storybook/stories/mixed/3_areas_and_bars.story.tsx @@ -8,14 +8,29 @@ import React from 'react'; -import { AreaSeries, Axis, BarSeries, Chart, CurveType, Position, ScaleType, Settings } from '@elastic/charts'; +import { + AreaSeries, + Axis, + BarSeries, + Chart, + CurveType, + LegendValue, + Position, + ScaleType, + Settings, +} from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/mixed/4_test_bar.story.tsx b/storybook/stories/mixed/4_test_bar.story.tsx index 98504f4bb5..448e7d4bfe 100644 --- a/storybook/stories/mixed/4_test_bar.story.tsx +++ b/storybook/stories/mixed/4_test_bar.story.tsx @@ -8,7 +8,7 @@ import React from 'react'; -import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -39,7 +39,12 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/mixed/5_test_bar_time.story.tsx b/storybook/stories/mixed/5_test_bar_time.story.tsx index 529719e721..2980dcca64 100644 --- a/storybook/stories/mixed/5_test_bar_time.story.tsx +++ b/storybook/stories/mixed/5_test_bar_time.story.tsx @@ -9,7 +9,7 @@ import { DateTime } from 'luxon'; import React from 'react'; -import { Axis, BarSeries, Chart, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, LineSeries, Position, ScaleType, Settings } from '@elastic/charts'; import { timeFormatter } from '@elastic/charts/src/utils/data/formatters'; import { ChartsStory } from '../../types'; @@ -43,7 +43,12 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + { { { }); return ( - + { ( baseTheme={useBaseTheme()} debug={boolean('Debug', true)} showLegend={boolean('Legend', true)} - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} legendPosition={select( 'Legend position', { diff --git a/storybook/stories/rotations/2_negative_ordinal.story.tsx b/storybook/stories/rotations/2_negative_ordinal.story.tsx index 58ef6ad0bc..5942b2a7e2 100644 --- a/storybook/stories/rotations/2_negative_ordinal.story.tsx +++ b/storybook/stories/rotations/2_negative_ordinal.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/3_rotations_ordinal.story.tsx b/storybook/stories/rotations/3_rotations_ordinal.story.tsx index 95bdbad46c..e27b77379a 100644 --- a/storybook/stories/rotations/3_rotations_ordinal.story.tsx +++ b/storybook/stories/rotations/3_rotations_ordinal.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/4_90_ordinal.story.tsx b/storybook/stories/rotations/4_90_ordinal.story.tsx index a3821ac83e..9b8ce6db5f 100644 --- a/storybook/stories/rotations/4_90_ordinal.story.tsx +++ b/storybook/stories/rotations/4_90_ordinal.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/5_180_ordinal.story.tsx b/storybook/stories/rotations/5_180_ordinal.story.tsx index f4795469ef..71b638e154 100644 --- a/storybook/stories/rotations/5_180_ordinal.story.tsx +++ b/storybook/stories/rotations/5_180_ordinal.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/6_negative_linear.story.tsx b/storybook/stories/rotations/6_negative_linear.story.tsx index d83dd0d86a..afeb5f6e1d 100644 --- a/storybook/stories/rotations/6_negative_linear.story.tsx +++ b/storybook/stories/rotations/6_negative_linear.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/7_rotations_linear.story.tsx b/storybook/stories/rotations/7_rotations_linear.story.tsx index a386762ced..711cd530e2 100644 --- a/storybook/stories/rotations/7_rotations_linear.story.tsx +++ b/storybook/stories/rotations/7_rotations_linear.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/8_90_deg_linear.story.tsx b/storybook/stories/rotations/8_90_deg_linear.story.tsx index 8e068d5440..9693b06df2 100644 --- a/storybook/stories/rotations/8_90_deg_linear.story.tsx +++ b/storybook/stories/rotations/8_90_deg_linear.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/rotations/9_180_deg_linear.story.tsx b/storybook/stories/rotations/9_180_deg_linear.story.tsx index 47349291d7..0828ad5680 100644 --- a/storybook/stories/rotations/9_180_deg_linear.story.tsx +++ b/storybook/stories/rotations/9_180_deg_linear.story.tsx @@ -8,14 +8,20 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, Position, ScaleType, Settings } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; export const Example: ChartsStory = (_, { title, description }) => ( - + diff --git a/storybook/stories/small_multiples/7_sunbursts.story.tsx b/storybook/stories/small_multiples/7_sunbursts.story.tsx index 5ff9b856e8..61d4cf7e3f 100644 --- a/storybook/stories/small_multiples/7_sunbursts.story.tsx +++ b/storybook/stories/small_multiples/7_sunbursts.story.tsx @@ -15,6 +15,7 @@ import { Datum, GroupBy, LegendStrategy, + LegendValue, PartialTheme, Partition, PartitionLayout, @@ -96,7 +97,7 @@ export const Example: ChartsStory = (_, { title, description }) => { { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/stylings/11_custom_lines.story.tsx b/storybook/stories/stylings/11_custom_lines.story.tsx index 7083a020bd..aea5a4c30c 100644 --- a/storybook/stories/stylings/11_custom_lines.story.tsx +++ b/storybook/stories/stylings/11_custom_lines.story.tsx @@ -18,6 +18,7 @@ import { Settings, LineSeriesStyle, RecursivePartial, + LegendValue, } from '@elastic/charts'; import { ChartsStory } from '../../types'; @@ -87,7 +88,7 @@ export const Example: ChartsStory = (_, { title, description }) => { { { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/stylings/13_custom_series_name_config.story.tsx b/storybook/stories/stylings/13_custom_series_name_config.story.tsx index 62a91f8be9..6a38c62a7b 100644 --- a/storybook/stories/stylings/13_custom_series_name_config.story.tsx +++ b/storybook/stories/stylings/13_custom_series_name_config.story.tsx @@ -8,7 +8,16 @@ import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings, SeriesNameConfigOptions } from '@elastic/charts'; +import { + Axis, + BarSeries, + Chart, + Position, + ScaleType, + Settings, + SeriesNameConfigOptions, + LegendValue, +} from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; import { ChartsStory } from '../../types'; @@ -33,7 +42,12 @@ export const Example: ChartsStory = (_, { title, description }) => { }; return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/stylings/14_custom_series_name_formatting.story.tsx b/storybook/stories/stylings/14_custom_series_name_formatting.story.tsx index 0e4e8d2dbf..db9933a2f3 100644 --- a/storybook/stories/stylings/14_custom_series_name_formatting.story.tsx +++ b/storybook/stories/stylings/14_custom_series_name_formatting.story.tsx @@ -10,7 +10,7 @@ import { DateTime } from 'luxon'; import moment from 'moment'; import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings, SeriesNameFn } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, SeriesNameFn, LegendValue } from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -55,7 +55,12 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/stylings/17_bar_series_color_variant.story.tsx b/storybook/stories/stylings/17_bar_series_color_variant.story.tsx index 3cb7621a04..e007c2892a 100644 --- a/storybook/stories/stylings/17_bar_series_color_variant.story.tsx +++ b/storybook/stories/stylings/17_bar_series_color_variant.story.tsx @@ -9,7 +9,7 @@ import { select, color } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, BarSeries, Chart, Position, ScaleType, Settings, PartialTheme } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Position, ScaleType, Settings, PartialTheme, LegendValue } from '@elastic/charts'; import { ColorVariant } from '@elastic/charts/src/utils/common'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; @@ -56,7 +56,7 @@ export const Example: ChartsStory = (_, { title, description }) => { { { { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/stylings/26_highlighter_style.story.tsx b/storybook/stories/stylings/26_highlighter_style.story.tsx index dee4d256c8..e2280b5939 100644 --- a/storybook/stories/stylings/26_highlighter_style.story.tsx +++ b/storybook/stories/stylings/26_highlighter_style.story.tsx @@ -18,6 +18,7 @@ import { Settings, timeFormatter, Tooltip, + LegendValue, } from '@elastic/charts'; import { KIBANA_METRICS } from '@elastic/charts/src/utils/data_samples/test_dataset_kibana'; @@ -30,7 +31,7 @@ export const Example: ChartsStory = (_, { title, description }) => ( { baseTheme={useBaseTheme()} debug={boolean('debug', true)} showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} legendPosition={Position.Right} /> { baseTheme={useBaseTheme()} debug={boolean('debug', false)} showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} legendPosition={Position.Right} /> diff --git a/storybook/stories/stylings/5_partial_custom_theme.story.tsx b/storybook/stories/stylings/5_partial_custom_theme.story.tsx index 06ad390c91..a2abc824d2 100644 --- a/storybook/stories/stylings/5_partial_custom_theme.story.tsx +++ b/storybook/stories/stylings/5_partial_custom_theme.story.tsx @@ -9,7 +9,7 @@ import { color } from '@storybook/addon-knobs'; import React from 'react'; -import { Axis, BarSeries, Chart, PartialTheme, Position, ScaleType, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, LegendValue, PartialTheme, Position, ScaleType, Settings } from '@elastic/charts'; import { SeededDataGenerator } from '@elastic/charts/src/mocks/utils'; import { ChartsStory } from '../../types'; @@ -35,7 +35,7 @@ export const Example: ChartsStory = (_, { title, description }) => { { { ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/stylings/9_custom_series_colors_function.story.tsx b/storybook/stories/stylings/9_custom_series_colors_function.story.tsx index e2d0a25420..b19403be83 100644 --- a/storybook/stories/stylings/9_custom_series_colors_function.story.tsx +++ b/storybook/stories/stylings/9_custom_series_colors_function.story.tsx @@ -18,6 +18,7 @@ import { Position, ScaleType, Settings, + LegendValue, } from '@elastic/charts'; import * as TestDatasets from '@elastic/charts/src/utils/data_samples/test_dataset'; @@ -48,7 +49,12 @@ export const Example: ChartsStory = (_, { title, description }) => { return ( - + Number(d).toFixed(2)} /> diff --git a/storybook/stories/sunburst/14_full_zero.story.tsx b/storybook/stories/sunburst/14_full_zero.story.tsx index 93c24306e6..0d67220e48 100644 --- a/storybook/stories/sunburst/14_full_zero.story.tsx +++ b/storybook/stories/sunburst/14_full_zero.story.tsx @@ -8,7 +8,15 @@ import React from 'react'; -import { Chart, Datum, Partition, PartitionLayout, Settings, defaultPartitionValueFormatter } from '@elastic/charts'; +import { + Chart, + Datum, + LegendValue, + Partition, + PartitionLayout, + Settings, + defaultPartitionValueFormatter, +} from '@elastic/charts'; import { ChartsStory } from '../../types'; import { useBaseTheme } from '../../use_base_theme'; @@ -16,7 +24,7 @@ import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '.. export const Example: ChartsStory = (_, { title, description }) => ( - + { hAlign: 'right', vAlign: 'top', }} - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} xDomain={xDomain} theme={{ scales: { @@ -121,7 +122,7 @@ export const Example: ChartsStory = (_, { title, description }) => { vAlign: 'top', }} showLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} xDomain={xDomain} theme={{ scales: { @@ -170,7 +171,7 @@ export const Example: ChartsStory = (_, { title, description }) => { hAlign: 'right', vAlign: 'top', }} - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} xDomain={xDomain} theme={{ scales: { diff --git a/storybook/stories/test_cases/7_rtl_text.story.tsx b/storybook/stories/test_cases/7_rtl_text.story.tsx index 60d927e3cb..3ad02921c6 100644 --- a/storybook/stories/test_cases/7_rtl_text.story.tsx +++ b/storybook/stories/test_cases/7_rtl_text.story.tsx @@ -21,6 +21,7 @@ import { Axis, Position, ScaleType, + LegendValue, } from '@elastic/charts'; import { mocks } from '@elastic/charts/src/mocks/hierarchical'; @@ -118,7 +119,7 @@ export const Example: ChartsStory = (_, { title, description }) => { rotation={type === SeriesType.Bar ? 90 : 0} debugState showLegend={showLegend} - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} baseTheme={useBaseTheme()} legendColorPicker={getColorPicker('leftCenter')} /> diff --git a/storybook/stories/treemap/9_zero_values.story.tsx b/storybook/stories/treemap/9_zero_values.story.tsx index c9e2798590..6283155784 100644 --- a/storybook/stories/treemap/9_zero_values.story.tsx +++ b/storybook/stories/treemap/9_zero_values.story.tsx @@ -8,7 +8,15 @@ import React from 'react'; -import { Chart, Datum, Partition, PartitionLayout, Settings, defaultPartitionValueFormatter } from '@elastic/charts'; +import { + Chart, + Datum, + LegendValue, + Partition, + PartitionLayout, + Settings, + defaultPartitionValueFormatter, +} from '@elastic/charts'; import { mocks } from '@elastic/charts/src/mocks/hierarchical'; import { ChartsStory } from '../../types'; @@ -17,7 +25,7 @@ import { indexInterpolatedFillColor, interpolatorCET2s, productLookup } from '.. export const Example: ChartsStory = (_, { title, description }) => ( - + (i ? d : { ...d, exportVal: 0 }))} diff --git a/storybook/stories/waffle/1_simple.story.tsx b/storybook/stories/waffle/1_simple.story.tsx index 8223e004d6..24a602b8d9 100644 --- a/storybook/stories/waffle/1_simple.story.tsx +++ b/storybook/stories/waffle/1_simple.story.tsx @@ -9,7 +9,15 @@ import { boolean } from '@storybook/addon-knobs'; import React from 'react'; -import { Chart, Datum, Partition, PartitionLayout, Settings, defaultPartitionValueFormatter } from '@elastic/charts'; +import { + Chart, + Datum, + LegendValue, + Partition, + PartitionLayout, + Settings, + defaultPartitionValueFormatter, +} from '@elastic/charts'; import { mocks } from '@elastic/charts/src/mocks/hierarchical'; import { getRandomNumberGenerator } from '@elastic/charts/src/mocks/utils'; @@ -36,7 +44,7 @@ export const Example: ChartsStory = (_, { title, description }) => { debug={showDebug} showLegend flatLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} /> { baseTheme={useBaseTheme()} showLegend flatLegend - showLegendExtra + legendValues={[LegendValue.CurrentAndLastValue]} />