From f6949489de5fc8e80649c6f1476b129df8880f5a Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Mon, 27 May 2024 14:28:28 +0200 Subject: [PATCH 1/7] feat(Legend): add legend stats --- .../legend/tabular-data-chrome-linux.png | Bin 0 -> 42543 bytes packages/charts/api/charts.api.md | 5 +- .../__snapshots__/partition.test.tsx.snap | 13 + .../layout/viewmodel/hierarchy_of_arrays.ts | 4 +- .../state/selectors/compute_legend.ts | 3 +- .../src/chart_types/xy_chart/legend/legend.ts | 39 +-- .../xy_chart/state/utils/common.test.ts | 8 +- ...get_last_value.ts => get_legend_values.ts} | 24 +- .../chart_types/xy_chart/tooltip/tooltip.ts | 4 +- packages/charts/src/common/legend.ts | 34 +- .../__snapshots__/chart.test.tsx.snap | 12 +- packages/charts/src/components/_index.scss | 1 + .../legend/__snapshots__/legend.test.tsx.snap | 84 ++--- .../src/components/legend/_legend_item.scss | 8 +- .../src/components/legend/_variables.scss | 16 +- .../charts/src/components/legend/label.tsx | 39 ++- .../src/components/legend/legend.test.tsx | 43 ++- .../charts/src/components/legend/legend.tsx | 49 ++- .../components/legend/legend_color_picker.tsx | 80 +++++ .../src/components/legend/legend_item.tsx | 320 ++++++------------ .../legend/legend_table/_index.scss | 4 + .../legend_table/_legend_single_item.scss | 76 +++++ .../legend/legend_table/_legend_table.scss | 49 +++ .../legend_table/_legend_table_cell.scss | 27 ++ .../legend_table/_legend_table_header.scss | 12 + .../components/legend/legend_table/index.tsx | 9 + .../legend/legend_table/legend_table.test.tsx | 52 +++ .../legend/legend_table/legend_table.tsx | 59 ++++ .../legend/legend_table/legend_table_body.tsx | 24 ++ .../legend/legend_table/legend_table_cell.tsx | 29 ++ .../legend_table/legend_table_header.tsx | 54 +++ .../legend/legend_table/legend_table_item.tsx | 110 ++++++ .../legend/legend_table/legend_table_row.tsx | 23 ++ .../legend/legend_table/legend_value.tsx | 18 + .../charts/src/components/legend/types.ts | 46 +++ .../charts/src/components/legend/utils.ts | 8 +- packages/charts/src/specs/settings.tsx | 4 + .../selectors/get_legend_config_selector.ts | 2 + .../src/state/selectors/get_legend_size.ts | 23 +- .../state/selectors/get_legend_table_size.ts | 124 +++++++ .../src/utils/data_samples/test_dataset.ts | 161 +++++++++ .../legend/11_legend_actions.story.tsx | 2 +- .../stories/legend/17_tabular_data.story.tsx | 118 +++++++ storybook/stories/legend/legend.stories.tsx | 1 + .../utils/components/get_color_picker.tsx | 1 + 45 files changed, 1471 insertions(+), 351 deletions(-) create mode 100644 e2e/screenshots/all.test.ts-snapshots/baselines/legend/tabular-data-chrome-linux.png rename packages/charts/src/chart_types/xy_chart/state/utils/{get_last_value.ts => get_legend_values.ts} (81%) create mode 100644 packages/charts/src/components/legend/legend_color_picker.tsx create mode 100644 packages/charts/src/components/legend/legend_table/_index.scss create mode 100644 packages/charts/src/components/legend/legend_table/_legend_single_item.scss create mode 100644 packages/charts/src/components/legend/legend_table/_legend_table.scss create mode 100644 packages/charts/src/components/legend/legend_table/_legend_table_cell.scss create mode 100644 packages/charts/src/components/legend/legend_table/_legend_table_header.scss create mode 100644 packages/charts/src/components/legend/legend_table/index.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table.test.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table_body.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table_cell.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table_header.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table_item.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_table_row.tsx create mode 100644 packages/charts/src/components/legend/legend_table/legend_value.tsx create mode 100644 packages/charts/src/components/legend/types.ts create mode 100644 packages/charts/src/state/selectors/get_legend_table_size.ts create mode 100644 storybook/stories/legend/17_tabular_data.story.tsx diff --git a/e2e/screenshots/all.test.ts-snapshots/baselines/legend/tabular-data-chrome-linux.png b/e2e/screenshots/all.test.ts-snapshots/baselines/legend/tabular-data-chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ae1432acf45a71c5978b5bf4ffe731b3c87407 GIT binary patch literal 42543 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 literal 0 HcmV?d00001 diff --git a/packages/charts/api/charts.api.md b/packages/charts/api/charts.api.md index fd0dab43ec..8cc5bcf163 100644 --- a/packages/charts/api/charts.api.md +++ b/packages/charts/api/charts.api.md @@ -1817,6 +1817,7 @@ export type LegendItemListener = (series: SeriesIdentifier[]) => void; export type LegendItemValue = { value: PrimitiveValue; label: string; + type: LegendValue; }; // @public (undocumented) @@ -1854,6 +1855,7 @@ export interface LegendSpec { legendSize: number; legendSort?: SeriesCompareFn; legendStrategy?: LegendStrategy; + legendTitle?: string; legendValues: Array; // (undocumented) onLegendItemClick?: LegendItemListener; @@ -1892,7 +1894,6 @@ export interface LegendStyle { // @public (undocumented) export const LegendValue: Readonly<{ - None: "none"; CurrentAndLastValue: "currentAndLastValue"; LastValue: "lastValue"; LastNonNullValue: "lastNonNullValue"; @@ -2751,7 +2752,7 @@ export const Settings: (props: SFProps; +export const settingsBuildProps: BuildProps; // @public (undocumented) export type SettingsProps = ComponentProps; 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 ceb5f5d1f1..c429c9808f 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 @@ -31,6 +31,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "2", + "type": "value", "value": 2, }, ], @@ -68,6 +69,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -105,6 +107,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -138,6 +141,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "2", + "type": "value", "value": 2, }, ], @@ -175,6 +179,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -212,6 +217,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -245,6 +251,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "2", + "type": "value", "value": 2, }, ], @@ -282,6 +289,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -319,6 +327,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems all distinct "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -357,6 +366,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -394,6 +404,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -432,6 +443,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "values": [ { "label": "1", + "type": "value", "value": 1, }, ], @@ -469,6 +481,7 @@ exports[`Retain hierarchy even with arbitrary names getLegendItems special case: "values": [ { "label": "1", + "type": "value", "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 86c7a20649..14e4d3f39c 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 @@ -7,7 +7,7 @@ */ import { isMosaic, isSunburst, isTreemap, isWaffle } from './viewmodel'; -import { LegendItemExtraValues } from '../../../../common/legend'; +import { LegendItemExtraValues, LegendValue } from '../../../../common/legend'; import { SeriesKey } from '../../../../common/series_id'; import { Relation } from '../../../../common/text_utils'; import { LegendPath } from '../../../../state/actions/legend'; @@ -118,7 +118,7 @@ export function getExtraValueMap( const { value, path, [CHILDREN_KEY]: children } = arrayNode; const values: LegendItemExtraValues = new Map(); const label = valueFormatter ? valueFormatter(value) : `${value}`; - values.set(key, { label, value }); + values.set(key, { label, value, type: LegendValue.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/selectors/compute_legend.ts b/packages/charts/src/chart_types/partition_chart/state/selectors/compute_legend.ts index 06dcd513d8..a0458849cf 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 @@ -10,7 +10,7 @@ import { getPartitionSpecs } from './get_partition_specs'; import { getTrees } from './tree'; import { RGBATupleToString } from '../../../../common/color_library_wrappers'; import { Colors } from '../../../../common/colors'; -import { LegendItem } from '../../../../common/legend'; +import { LegendItem, LegendValue } from '../../../../common/legend'; import { SeriesIdentifier } from '../../../../common/series_id'; import { createCustomCachedSelector } from '../../../../state/create_selector'; import { getLegendConfigSelector } from '../../../../state/selectors/get_legend_config_selector'; @@ -127,6 +127,7 @@ function walkTree( { value: node[AGGREGATE_KEY], label: valueFormatter(node[AGGREGATE_KEY]), + type: LegendValue.Value, }, ], }, 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 98bc2144e5..f57cfaebb7 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, LegendValue } from '../../../common/legend'; +import { LegendItem } from '../../../common/legend'; import { SeriesKey, SeriesIdentifier } from '../../../common/series_id'; import { SettingsSpec } from '../../../specs'; import { isDefined, mergePartial } from '../../../utils/common'; @@ -16,7 +16,7 @@ import { getLegendCompareFn, SeriesCompareFn } from '../../../utils/series_sort' import { PointStyle, Theme } from '../../../utils/themes/theme'; import { XDomain } from '../domains/types'; import { isDatumFilled } from '../rendering/utils'; -import { getLegendValue } from '../state/utils/get_last_value'; +import { getLegendValues } from '../state/utils/get_legend_values'; import { getAxesSpecForSpecId, getSpecsById } from '../state/utils/spec'; import { Y0_ACCESSOR_POSTFIX, Y1_ACCESSOR_POSTFIX } from '../tooltip/tooltip'; import { defaultTickFormatter } from '../utils/axis_utils'; @@ -109,7 +109,7 @@ export function computeLegend( const legendItems: LegendItem[] = []; const defaultColor = theme.colors.defaultVizColor; - const legendValueMode = settingsSpec.legendValues[0] ?? LegendValue.None; + const legendValues = settingsSpec.legendValues ?? []; dataSeries.forEach((series) => { const { specId, yAccessor } = series; @@ -140,8 +140,7 @@ export function computeLegend( const pointStyle = getPointStyle(spec, theme); - const itemValue = getLegendValue(series, xDomain, legendValueMode, y1Accessor(series.stackMode)); - const formattedItemValue = itemValue !== null ? formatter(itemValue) : ''; + const legendValuesItems = getLegendValues(series, xDomain, legendValues, y1Accessor(series.stackMode), formatter); legendItems.push({ depth: 0, @@ -152,22 +151,19 @@ export function computeLegend( isSeriesHidden, isItemHidden: hideInLegend, isToggleable: true, - values: - itemValue !== null - ? [ - { - value: itemValue, - label: formattedItemValue, - }, - ] - : [], + values: legendValuesItems, path: [{ index: 0, value: seriesIdentifier.key }], keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()], pointStyle, }); if (banded) { - const bandedItemValue = getLegendValue(series, xDomain, legendValueMode, y0Accessor(series.stackMode)); - const bandedFormattedItemValue = bandedItemValue !== null ? formatter(bandedItemValue) : ''; + const bandedLegendValuesItems = getLegendValues( + series, + xDomain, + legendValues, + y0Accessor(series.stackMode), + formatter, + ); const labelY0 = getBandedLegendItemLabel(name, BandedAccessorType.Y0, postFixes); legendItems.push({ @@ -179,15 +175,7 @@ export function computeLegend( isSeriesHidden, isItemHidden: hideInLegend, isToggleable: true, - values: - bandedItemValue !== null - ? [ - { - value: bandedItemValue, - label: bandedFormattedItemValue, - }, - ] - : [], + values: bandedLegendValuesItems, path: [{ index: 0, value: seriesIdentifier.key }], keys: [specId, spec.groupId, yAccessor, ...series.splitAccessors.values()], pointStyle, @@ -201,7 +189,6 @@ export function computeLegend( return defaultXYLegendSeriesSort(aDs, bDs); }); const sortFn: SeriesCompareFn = settingsSpec.legendSort ?? legendSortFn; - return groupBy( legendItems.sort((a, b) => a.seriesIdentifiers[0] && b.seriesIdentifiers[0] ? sortFn(a.seriesIdentifiers[0], b.seriesIdentifiers[0]) : 0, 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 fe4ff09579..32da79c1f9 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 @@ -134,7 +134,7 @@ describe('Type Checks', () => { specId: 'bars', }, ], - values: [{ value: 6, label: '6.00' }], + values: [{ value: 6, label: '6.00', type: 'currentAndLastValue' }], isSeriesHidden: true, path: [], keys: [], @@ -149,7 +149,7 @@ describe('Type Checks', () => { specId: 'bars', }, ], - values: [{ value: 2, label: '2.00' }], + values: [{ value: 2, label: '2.00', type: 'currentAndLastValue' }], isSeriesHidden: true, path: [], keys: [], @@ -169,7 +169,7 @@ describe('Type Checks', () => { specId: 'bars', }, ], - values: [{ value: 6, label: '6.00' }], + values: [{ value: 6, label: '6.00', type: 'currentAndLastValue' }], isSeriesHidden: false, path: [], keys: [], @@ -184,7 +184,7 @@ describe('Type Checks', () => { specId: 'bars', }, ], - values: [{ value: 2, label: '2.00' }], + values: [{ value: 2, label: '2.00', type: 'currentAndLastValue' }], 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_legend_values.ts similarity index 81% rename from packages/charts/src/chart_types/xy_chart/state/utils/get_last_value.ts rename to packages/charts/src/chart_types/xy_chart/state/utils/get_legend_values.ts index 5a283ad987..9ebcd207f1 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_legend_values.ts @@ -26,6 +26,29 @@ import { LegendValue } from '../../../../common/legend'; import { ScaleType } from '../../../../scales/constants'; import { XDomain } from '../../domains/types'; import { DataSeries, DataSeriesDatum } from '../../utils/series'; +import { TickFormatter } from '../../utils/specs'; + +/** + * This method return legend values 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. + * @internal + */ +export function getLegendValues( + series: DataSeries, + xDomain: XDomain, + types: LegendValue[], + valueAccessor: (d: DataSeriesDatum) => number | null, + formatter: TickFormatter | ((tick: unknown) => string), +) { + return types.map((type) => { + const value = getLegendValue(series, xDomain, type, valueAccessor); + return { + type, + label: typeof value === 'number' ? formatter(value) : '', + value, + }; + }); +} /** * This method return a value from a DataSeries that correspond to the type of value requested. @@ -80,7 +103,6 @@ export function getLegendValue( 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 87537213c7..334e09f4db 100644 --- a/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts +++ b/packages/charts/src/chart_types/xy_chart/tooltip/tooltip.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { LegendItemExtraValues } from '../../../common/legend'; +import { LegendItemExtraValues, LegendValue } from '../../../common/legend'; import { SeriesKey } from '../../../common/series_id'; import { TooltipValue } from '../../../specs'; import { PointerValue } from '../../../state/types'; @@ -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, { label: formattedValue, value }); + current.set(valueAccessor, { label: formattedValue, value, type: LegendValue.CurrentAndLastValue }); } seriesTooltipValues.set(seriesIdentifier.key, current); }); diff --git a/packages/charts/src/common/legend.ts b/packages/charts/src/common/legend.ts index dcc9ab0360..308713d7e6 100644 --- a/packages/charts/src/common/legend.ts +++ b/packages/charts/src/common/legend.ts @@ -20,11 +20,10 @@ import { PointStyle } from '../utils/themes/theme'; export type LegendItemChildId = CategoryKey; /** @public */ -export type LegendItemValue = { value: PrimitiveValue; label: string }; +export type LegendItemValue = { value: PrimitiveValue; label: string; type: LegendValue }; /** @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 */ @@ -81,7 +80,7 @@ export type LegendItem = { label: CategoryLabel; isSeriesHidden?: boolean; isItemHidden?: boolean; - values: Array; + values: LegendItemValue[]; // TODO: Remove when partition layers are toggleable isToggleable?: boolean; keys: Array; @@ -91,3 +90,32 @@ export type LegendItem = { /** @internal */ export type LegendItemExtraValues = Map; + +/** @internal */ +export const shouldDisplayTable = (legendValues: LegendValue[]) => + legendValues.some((v) => v !== LegendValue.CurrentAndLastValue && v !== LegendValue.Value); +/** + * todo: i18n + * @internal + */ +export const legendValueTitlesMap = { + [LegendValue.CurrentAndLastValue]: 'Value', + [LegendValue.Value]: 'Value', + [LegendValue.Percent]: 'Percent', + [LegendValue.LastValue]: 'Last', + [LegendValue.LastNonNullValue]: 'Last non-null', + [LegendValue.FirstValue]: 'First', + [LegendValue.FirstNonNullValue]: 'First non-null', + [LegendValue.Average]: 'Avg', + [LegendValue.Median]: 'Mid', + [LegendValue.Min]: 'Min', + [LegendValue.Max]: 'Max', + [LegendValue.Total]: 'Total', + [LegendValue.Count]: 'Count', + [LegendValue.DistinctCount]: 'Dist Count', + [LegendValue.Variance]: 'Variance', + [LegendValue.StdDeviation]: 'Std dev', + [LegendValue.Range]: 'Range', + [LegendValue.Difference]: 'Diff', + [LegendValue.DifferencePercent]: 'Diff %', +}; diff --git a/packages/charts/src/components/__snapshots__/chart.test.tsx.snap b/packages/charts/src/components/__snapshots__/chart.test.tsx.snap index 635170f314..f4f071aa88 100644 --- a/packages/charts/src/components/__snapshots__/chart.test.tsx.snap +++ b/packages/charts/src/components/__snapshots__/chart.test.tsx.snap @@ -26,10 +26,10 @@ exports[`Chart should render the legend name test 1`] = `
    - -
  • + +
  • -
    +
    @@ -42,13 +42,13 @@ exports[`Chart should render the legend name test 1`] = `
    -
  • -
    +
diff --git a/packages/charts/src/components/_index.scss b/packages/charts/src/components/_index.scss index 587bf75fa1..9227a6eb92 100644 --- a/packages/charts/src/components/_index.scss +++ b/packages/charts/src/components/_index.scss @@ -2,6 +2,7 @@ @import 'container'; @import 'brush/index'; @import 'tooltip/components/index'; +@import 'legend/legend_table/index'; @import 'portal/index'; @import 'icons/index'; @import 'legend/index'; 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 e865ba3aa8..b69fc93ed9 100644 --- a/packages/charts/src/components/legend/__snapshots__/legend.test.tsx.snap +++ b/packages/charts/src/components/legend/__snapshots__/legend.test.tsx.snap @@ -1,10 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Legend #legendColorPicker should match snapshot after onChange is called 1`] = ` -" -
  • +" +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    " +" `; exports[`Legend #legendColorPicker should match snapshot after onClose is called 1`] = ` -" -
  • +" +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    " +" `; exports[`Legend #legendColorPicker should render colorPicker when color is clicked 1`] = ` @@ -192,17 +192,17 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click - " `; exports[`Legend #legendColorPicker should render colorPicker when color is clicked 2`] = ` -" -
  • +" +
  • -
    +
  • - +
    Custom Color Picker @@ -229,15 +229,15 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click -
    -
    -
  • + +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    -
  • + +
  • -
    +
  • -
    " +" `; diff --git a/packages/charts/src/components/legend/_legend_item.scss b/packages/charts/src/components/legend/_legend_item.scss index f85d1f3d0e..97ee4aae81 100644 --- a/packages/charts/src/components/legend/_legend_item.scss +++ b/packages/charts/src/components/legend/_legend_item.scss @@ -12,7 +12,7 @@ $legendItemHeight: #{$euiFontSizeXS * $euiLineHeight}; position: relative; // wrapper is needed to isolate color icon when wrapped or not - .colorWrapper > *:first-of-type { + .echLegend__colorWrapper > *:first-of-type { // euiPopover adds a div with height of 19px otherwise // this prevents color dot from shifting when wrapped height: $legendItemHeight; @@ -21,7 +21,7 @@ $legendItemHeight: #{$euiFontSizeXS * $euiLineHeight}; &:not([dir='rtl']) > *:not(.background) { margin-left: $euiSizeXS; - &:last-child:not(.echLegendItem__extra) { + &:last-child:not(.echLegendItem__legendValue) { margin-right: $euiSizeXS; } } @@ -29,7 +29,7 @@ $legendItemHeight: #{$euiFontSizeXS * $euiLineHeight}; &[dir='rtl'] > *:not(.background) { margin-right: $euiSizeXS; - &:last-child:not(.echLegendItem__extra) { + &:last-child:not(.echLegendItem__legendValue) { margin-left: $euiSizeXS; } } @@ -103,7 +103,7 @@ $legendItemHeight: #{$euiFontSizeXS * $euiLineHeight}; } } - &__extra { + &__legendValue { @include euiFontSizeXS; text-align: right; flex: 0 0 auto; diff --git a/packages/charts/src/components/legend/_variables.scss b/packages/charts/src/components/legend/_variables.scss index c8543808be..2fb0e563c7 100644 --- a/packages/charts/src/components/legend/_variables.scss +++ b/packages/charts/src/components/legend/_variables.scss @@ -1,3 +1,15 @@ -$echLegendMaxWidth: 200px; -$echLegendRowGap: 8px; $echLegendColumnGap: 24px; +$echLegendRowHeight: 16px; + +$tableRowHoverColor: $euiColorLightestShade; +$legendBorderColor: $euiColorLightestShade; + +$echLegendRowGap: 8px; +$legendItemVerticalPadding: $echLegendRowGap / 2; +$echLegendTableCellPadding: 8px 4px; + +$echLegendTablePadding: 8px; +$echLegendHorizontalTablePadding: 4px 8px 4px 16px; +$legendBorderWidth: 1px; +$tableBorder: solid $legendBorderWidth $legendBorderColor; +$tableOutsideBorder: solid $legendBorderWidth $euiColorLightShade; diff --git a/packages/charts/src/components/legend/label.tsx b/packages/charts/src/components/legend/label.tsx index 0b8ac8ecc6..9016586955 100644 --- a/packages/charts/src/components/legend/label.tsx +++ b/packages/charts/src/components/legend/label.tsx @@ -57,19 +57,14 @@ ${modifierKey} + click: ${showSeriesMessage}`; */ export function Label({ label, - isToggleable, onToggle, + isToggleable, isSeriesHidden, options, hiddenSeriesCount, totalSeriesCount, }: LabelProps) { - const maxLines = Math.abs(options.maxLines); - const labelClassNames = classNames('echLegendItem__label', { - 'echLegendItem__label--clickable': Boolean(onToggle), - 'echLegendItem__label--singleline': maxLines === 1, - 'echLegendItem__label--multiline': maxLines > 1, - }); + const { className, dir, clampStyles } = getSharedProps(label, options, !!onToggle); const onClick: MouseEventHandler = useCallback( ({ metaKey, ctrlKey }) => onToggle?.(isAppleDevice ? metaKey : ctrlKey), @@ -82,9 +77,7 @@ export function Label({ [onToggle], ); - const dir = isRTLString(label) ? 'rtl' : 'ltr'; // forced for individual labels in case mixed charset const title = options.maxLines > 0 ? label : ''; // full text already visible - const clampStyles = maxLines > 1 ? { WebkitLineClamp: maxLines } : {}; const interactionsGuidanceText = getInteractivityTitle(!isSeriesHidden, hiddenSeriesCount, totalSeriesCount); @@ -95,7 +88,7 @@ export function Label({ role="button" tabIndex={0} dir={dir} - className={labelClassNames} + className={className} title={`${title}${interactionsGuidanceText}`} onClick={onClick} onKeyDown={onKeyDown} @@ -106,8 +99,32 @@ export function Label({ {label} ) : ( -
    +
    {label}
    ); } + +/** @internal */ +export function NonInteractiveLabel({ label, options }: { label: string; options: LegendLabelOptions }) { + const { className, dir, clampStyles } = getSharedProps(label, options); + return ( +
    + {label} +
    + ); +} + +function getSharedProps(label: string, options: LegendLabelOptions, isToggleable?: boolean) { + const maxLines = Math.abs(options.maxLines); + const className = classNames('echLegendItem__label', { + 'echLegendItem__label--clickable': Boolean(isToggleable), + 'echLegendItem__label--singleline': maxLines === 1, + 'echLegendItem__label--multiline': maxLines > 1, + }); + + const dir = isRTLString(label) ? 'rtl' : 'ltr'; // forced for individual labels in case mixed charset + const clampStyles = maxLines > 1 ? { WebkitLineClamp: maxLines } : {}; + + return { className, dir, clampStyles }; +} diff --git a/packages/charts/src/components/legend/legend.test.tsx b/packages/charts/src/components/legend/legend.test.tsx index b60e4a8e91..6e7afd8ca5 100644 --- a/packages/charts/src/components/legend/legend.test.tsx +++ b/packages/charts/src/components/legend/legend.test.tsx @@ -11,6 +11,8 @@ import React, { Component } from 'react'; import { Legend } from './legend'; import { LegendListItem } from './legend_item'; +import { LegendTable } from './legend_table'; +import { LegendTableRow } from './legend_table/legend_table_row'; import { LegendValue } from '../../common/legend'; import { SeededDataGenerator } from '../../mocks/utils'; import { ScaleType } from '../../scales/constants'; @@ -23,7 +25,7 @@ describe('Legend', () => { it('shall render the all the series names', () => { const wrapper = mount( - + { @@ -123,7 +125,7 @@ describe('Legend', () => { const data = dg.generateGroupedSeries(10, numberOfSeries, 'split'); const wrapper = mount( - + { const data = [{ x: 2, y: 5 }]; const wrapper = mount( - + { }); }); }); + describe('legend table', () => { + it('should render legend table when there is a legend value that is not CurrentAndLastValue', () => { + const wrapper = mount( + + + + , + ); + const legendTable = wrapper.find(LegendTable); + expect(legendTable.exists).toBeTruthy(); + const legendRows = legendTable.find(LegendTableRow); + expect(legendRows.exists).toBeTruthy(); + expect(legendRows).toHaveLength(5); + const expected = ['Min', 'group0123', 'group1123', 'group2123', 'group3123']; + legendRows.forEach((row, i) => { + expect(row.text()).toBe(expected[i]); + }); + }); + }); }); diff --git a/packages/charts/src/components/legend/legend.tsx b/packages/charts/src/components/legend/legend.tsx index ebdd32b9ce..f047ae548d 100644 --- a/packages/charts/src/components/legend/legend.tsx +++ b/packages/charts/src/components/legend/legend.tsx @@ -7,21 +7,24 @@ */ import classNames from 'classnames'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { Dispatch, bindActionCreators } from 'redux'; import { CustomLegend } from './custom_legend'; import { LegendItemProps, LegendListItem } from './legend_item'; +import { LegendTable } from './legend_table'; import { getLegendPositionConfig, legendPositionStyle } from './position_style'; import { getLegendStyle, getLegendListStyle } from './style_utils'; -import { LegendItem, LegendItemExtraValues } from '../../common/legend'; +import { LegendItem, LegendItemExtraValues, shouldDisplayTable } from '../../common/legend'; +import { SeriesIdentifier } from '../../common/series_id'; import { DEFAULT_LEGEND_CONFIG, LegendSpec } from '../../specs'; import { clearTemporaryColors, setTemporaryColor, setPersistedColor } from '../../state/actions/colors'; import { onToggleDeselectSeriesAction, onLegendItemOutAction, onLegendItemOverAction, + LegendPath, } from '../../state/actions/legend'; import { GlobalChartState } from '../../state/chart_state'; import { getChartThemeSelector } from '../../state/selectors/get_chart_theme'; @@ -45,7 +48,7 @@ interface LegendStateProps { chartDimensions: Dimensions; containerDimensions: Dimensions; chartTheme: Theme; - size: Size; + size: Size & { seriesWidth?: number }; config: LegendSpec; items: LegendItem[]; extraValues: Map; @@ -72,6 +75,28 @@ function LegendComponent(props: LegendStateProps & LegendDispatchProps) { config, } = props; + const { onLegendItemOut, onLegendItemOver } = config; + const { onItemOutAction, onItemOverAction } = props; + + const onLegendItemMouseOver = useCallback( + (seriesIdentifiers: SeriesIdentifier[], path: LegendPath) => { + // call the settings listener directly if available + if (onLegendItemOver) { + onLegendItemOver(seriesIdentifiers); + } + onItemOverAction(path); + }, + [onItemOverAction, onLegendItemOver], + ); + + const onLegendItemMouseOut = useCallback(() => { + // call the settings listener directly if available + if (onLegendItemOut) { + onLegendItemOut(); + } + onItemOutAction(); + }, [onLegendItemOut, onItemOutAction]); + if (items.every(({ isItemHidden }) => isItemHidden)) { return null; } @@ -99,21 +124,23 @@ function LegendComponent(props: LegendStateProps & LegendDispatchProps) { hiddenItems: items.filter(({ isSeriesHidden }) => isSeriesHidden).length, extraValues: props.extraValues, legendValues: config.legendValues, - onMouseOut: config.onLegendItemOut, - onMouseOver: config.onLegendItemOver, + onLegendItemMouseOver, + onLegendItemMouseOut, onClick: config.onLegendItemClick, clearTemporaryColorsAction: props.clearTemporaryColors, setPersistedColorAction: props.setPersistedColor, setTemporaryColorAction: props.setTemporaryColor, - mouseOutAction: props.onItemOutAction, - mouseOverAction: props.onItemOverAction, toggleDeselectSeriesAction: props.onToggleDeselectSeriesAction, colorPicker: config.legendColorPicker, action: config.legendAction, labelOptions: legend.labelOptions, flatLegend: config.flatLegend ?? DEFAULT_LEGEND_CONFIG.flatLegend, + legendTitle: config.legendTitle, }; + const positionStyle = legendPositionStyle(config, size, chartDimensions, containerDimensions); + const isTableView = shouldDisplayTable(itemProps.legendValues); + return (
    {config.customLegend ? ( @@ -125,12 +152,16 @@ function LegendComponent(props: LegendStateProps & LegendDispatchProps) { seriesIdentifiers, path, extraValue: itemProps.extraValues.get(seriesIdentifiers[0]?.key ?? '')?.get(childId ?? ''), - onItemOutAction: itemProps.mouseOutAction, - onItemOverActon: () => itemProps.mouseOverAction(path), + onItemOutAction, + onItemOverActon: () => onItemOverAction(path), onItemClickAction: (negate: boolean) => itemProps.toggleDeselectSeriesAction(seriesIdentifiers, negate), }))} />
    + ) : isTableView ? ( +
    + +
    ) : (