diff --git a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts index 1135708db8c22..79a356ddad934 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/expression_functions/heatmap_legend.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { EXPRESSION_HEATMAP_LEGEND_NAME } from '../constants'; import { HeatmapLegendConfig, HeatmapLegendConfigResult } from '../types'; @@ -52,10 +53,19 @@ export const heatmapLegendConfig: ExpressionFunctionDefinition< }), }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: i18n.translate('expressionHeatmap.function.args.legendSize.help', { - defaultMessage: 'Specifies the legend size in pixels.', + defaultMessage: 'Specifies the legend size.', }), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, }, fn(input, args) { diff --git a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts index d3e7444ad08f2..19f63f9df9890 100644 --- a/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts @@ -15,6 +15,7 @@ import { import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import { CustomPaletteState } from '@kbn/charts-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { EXPRESSION_HEATMAP_NAME, EXPRESSION_HEATMAP_LEGEND_NAME, @@ -43,7 +44,7 @@ export interface HeatmapLegendConfig { * Exact legend width (vertical) or height (horizontal) * Limited to max of 70% of the chart container dimension Vertical legends limited to min of 30% of computed width */ - legendSize?: number; + legendSize?: LegendSize; } export type HeatmapLegendConfigResult = HeatmapLegendConfig & { diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx index 4f3e77b8f1d6e..19a57272116c8 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx @@ -17,6 +17,7 @@ import { findTestSubject } from '@elastic/eui/lib/test'; import { act } from 'react-dom/test-utils'; import { HeatmapRenderProps, HeatmapArguments } from '../../common'; import HeatmapComponent from './heatmap_component'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; jest.mock('@elastic/charts', () => { const original = jest.requireActual('@elastic/charts'); @@ -47,6 +48,7 @@ const args: HeatmapArguments = { isVisible: true, position: 'top', type: 'heatmap_legend', + legendSize: LegendSize.SMALL, }, gridConfig: { isCellLabelVisible: true, @@ -119,6 +121,33 @@ describe('HeatmapComponent', function () { expect(component.find(Settings).prop('legendPosition')).toEqual('top'); }); + it('sets correct legend sizes', () => { + const component = shallowWithIntl(); + expect(component.find(Settings).prop('legendSize')).toEqual(80); + + component.setProps({ + args: { + ...args, + legend: { + ...args.legend, + legendSize: LegendSize.AUTO, + }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toBeUndefined(); + + component.setProps({ + args: { + ...args, + legend: { + ...args.legend, + legendSize: undefined, + }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toEqual(130); + }); + it('renders the legend toggle component if uiState is set', async () => { const component = mountWithIntl(); await actWithTimeout(async () => { diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index a9b70d1bc2edd..36270ef896e46 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -29,6 +29,10 @@ import { getAccessorByDimension, getFormatByAccessor, } from '@kbn/visualizations-plugin/common/utils'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, +} from '@kbn/visualizations-plugin/common/constants'; import type { HeatmapRenderProps, FilterEvent, BrushEvent } from '../../common'; import { applyPaletteParams, findMinMaxByColumnId, getSortPredicate } from './helpers'; import { @@ -485,7 +489,7 @@ export const HeatmapComponent: FC = memo( onElementClick={interactive ? (onElementClick as ElementClickListener) : undefined} showLegend={showLegend ?? args.legend.isVisible} legendPosition={args.legend.position} - legendSize={args.legend.legendSize} + legendSize={LegendSizeToPixels[args.legend.legendSize ?? DEFAULT_LEGEND_SIZE]} legendColorPicker={uiState ? LegendColorPickerWrapper : undefined} debugState={window._echDebugStateFlag ?? false} tooltip={tooltip} diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap index 81ada60a772cd..2a06459822a0e 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/mosaic_vis_function.test.ts.snap @@ -112,7 +112,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "medium", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap index 28d5f35c89cbf..0f64f4c0a4779 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/pie_vis_function.test.ts.snap @@ -112,7 +112,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "small", "maxLegendLines": 2, "metric": Object { "accessor": 0, @@ -246,7 +246,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "small", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap index e1d9f98f57209..9f6210f42b48a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/treemap_vis_function.test.ts.snap @@ -112,7 +112,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "medium", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap index 33525b33f6f96..9cdc69904460a 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/__snapshots__/waffle_vis_function.test.ts.snap @@ -86,7 +86,7 @@ Object { }, "legendDisplay": "show", "legendPosition": "right", - "legendSize": undefined, + "legendSize": "medium", "maxLegendLines": 2, "metric": Object { "accessor": 0, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts index 250d0f1033ffe..d7839d1f7d1e9 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/i18n.ts @@ -47,7 +47,7 @@ export const strings = { }), getLegendSizeArgHelp: () => i18n.translate('expressionPartitionVis.reusable.function.args.legendSizeHelpText', { - defaultMessage: 'Specifies the legend size in pixels', + defaultMessage: 'Specifies the legend size', }), getNestedLegendArgHelp: () => i18n.translate('expressionPartitionVis.reusable.function.args.nestedLegendHelpText', { diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts index 74a85dd01e6e4..2f08ecb28c931 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/mosaic_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, MosaicVisExpressionFunctionDefinition } from '../types'; import { @@ -64,8 +65,17 @@ export const mosaicVisFunction = (): MosaicVisExpressionFunctionDefinition => ({ strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, nestedLegend: { types: ['boolean'], diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts index c542a25c30875..58ba8e837d339 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.test.ts @@ -14,7 +14,7 @@ import { ValueFormats, LegendDisplay, } from '../types/expression_renderers'; -import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +import { ExpressionValueVisDimension, LegendSize } from '@kbn/visualizations-plugin/common'; import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs'; import { pieVisFunction } from './pie_vis_function'; import { PARTITION_LABELS_VALUE } from '../constants'; @@ -31,6 +31,7 @@ describe('interpreter/functions#pieVis', () => { addTooltip: true, legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', + legendSize: LegendSize.SMALL, isDonut: true, emptySizeRatio: EmptySizeRatios.SMALL, nestedLegend: true, diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts index 9a30008cc6bb3..707334466ea99 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/pie_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { EmptySizeRatios, LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, PieVisExpressionFunctionDefinition } from '../types'; import { @@ -64,8 +65,17 @@ export const pieVisFunction = (): PieVisExpressionFunctionDefinition => ({ strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, nestedLegend: { types: ['boolean'], diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts index 062cf7e78b4ea..ab6f0c962e205 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/treemap_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, TreemapVisExpressionFunctionDefinition } from '../types'; import { @@ -64,8 +65,17 @@ export const treemapVisFunction = (): TreemapVisExpressionFunctionDefinition => strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, nestedLegend: { types: ['boolean'], diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts index 2f947a3d5fea6..0311f5466142f 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/waffle_vis_function.ts @@ -8,6 +8,7 @@ import { Position } from '@elastic/charts'; import { prepareLogTable, validateAccessor } from '@kbn/visualizations-plugin/common/utils'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LegendDisplay, PartitionVisParams } from '../types/expression_renderers'; import { ChartTypes, WaffleVisExpressionFunctionDefinition } from '../types'; import { @@ -63,8 +64,17 @@ export const waffleVisFunction = (): WaffleVisExpressionFunctionDefinition => ({ strict: true, }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: strings.getLegendSizeArgHelp(), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, truncateLegend: { types: ['boolean'], diff --git a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts index 05613af4f2f33..89a242fe26de1 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/common/types/expression_renderers.ts @@ -11,6 +11,7 @@ import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { ChartTypes, ExpressionValuePartitionLabels } from './expression_functions'; export enum EmptySizeRatios { @@ -52,7 +53,7 @@ interface VisCommonParams { legendPosition: Position; truncateLegend: boolean; maxLegendLines: number; - legendSize?: number; + legendSize?: LegendSize; ariaLabel?: string; } diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 3c48d3cb36771..0fcee477c99de 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -247,6 +247,7 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -674,6 +675,7 @@ exports[`PartitionVisComponent should render correct structure for mosaic 1`] = legendAction={[Function]} legendColorPicker={[Function]} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -1054,6 +1056,7 @@ exports[`PartitionVisComponent should render correct structure for pie 1`] = ` legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -1465,6 +1468,7 @@ exports[`PartitionVisComponent should render correct structure for treemap 1`] = legendAction={[Function]} legendColorPicker={[Function]} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} @@ -1866,6 +1870,7 @@ exports[`PartitionVisComponent should render correct structure for waffle 1`] = legendColorPicker={[Function]} legendMaxDepth={1} legendPosition="right" + legendSize={130} onElementClick={[Function]} onRenderChange={[Function]} showLegend={true} diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx index 648df546b2992..70c120e4fd759 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.test.tsx @@ -25,6 +25,7 @@ import { createMockWaffleParams, } from '../mocks'; import { ChartTypes } from '../../common/types'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; jest.mock('@elastic/charts', () => { const original = jest.requireActual('@elastic/charts'); @@ -177,6 +178,35 @@ describe('PartitionVisComponent', function () { expect(component.find(Settings).prop('legendMaxDepth')).toBeUndefined(); }); + it('sets correct legend sizes', () => { + const component = shallow( + + ); + expect(component.find(Settings).prop('legendSize')).toEqual(80); + + component.setProps({ + visParams: { + ...visParams, + legendSize: LegendSize.AUTO, + }, + }); + expect(component.find(Settings).prop('legendSize')).toBeUndefined(); + + component.setProps({ + visParams: { + ...visParams, + legendSize: undefined, + }, + }); + expect(component.find(Settings).prop('legendSize')).toEqual(130); + }); + it('defaults on displaying the tooltip', () => { const component = shallow(); expect(component.find(Settings).prop('tooltip')).toStrictEqual({ type: TooltipType.Follow }); diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index ef6d0d1c4525c..d25126869e087 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -22,7 +22,11 @@ import { import { useEuiTheme } from '@elastic/eui'; import type { PaletteRegistry } from '@kbn/coloring'; import { LegendToggle, ChartsPluginSetup } from '@kbn/charts-plugin/public'; -import type { PersistedState } from '@kbn/visualizations-plugin/public'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, +} from '@kbn/visualizations-plugin/common/constants'; +import { PersistedState } from '@kbn/visualizations-plugin/public'; import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils'; import { Datatable, @@ -387,7 +391,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { showLegend ?? shouldShowLegend(visType, visParams.legendDisplay, bucketColumns) } legendPosition={legendPosition} - legendSize={visParams.legendSize} + legendSize={LegendSizeToPixels[visParams.legendSize ?? DEFAULT_LEGEND_SIZE]} legendMaxDepth={visParams.nestedLegend ? undefined : 1} legendColorPicker={props.uiState ? LegendColorPickerWrapper : undefined} flatLegend={flatLegend} diff --git a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts index 2b383f1899d44..ddb46d5e55f13 100644 --- a/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts +++ b/src/plugins/chart_expressions/expression_xy/common/expression_functions/legend_config.ts @@ -8,6 +8,7 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/common/constants'; import { LEGEND_CONFIG } from '../constants'; import { LegendConfigFn } from '../types'; @@ -85,10 +86,19 @@ export const legendConfigFunction: LegendConfigFn = { }), }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: i18n.translate('expressionXY.legendConfig.legendSize.help', { - defaultMessage: 'Specifies the legend size in pixels.', + defaultMessage: 'Specifies the legend size.', }), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, }, async fn(input, args, handlers) { diff --git a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts index f7f7c5bcd1544..86eb173d4d4ed 100644 --- a/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts +++ b/src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts @@ -10,6 +10,7 @@ import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/chart import { $Values } from '@kbn/utility-types'; import type { PaletteOutput } from '@kbn/coloring'; import { Datatable, ExpressionFunctionDefinition } from '@kbn/expressions-plugin'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common'; import { AxisExtentModes, @@ -170,7 +171,7 @@ export interface LegendConfig { * Exact legend width (vertical) or height (horizontal) * Limited to max of 70% of the chart container dimension Vertical legends limited to min of 30% of computed width */ - legendSize?: number; + legendSize?: LegendSize; } export interface LabelsOrientationConfig { diff --git a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap index 3b11ee812da6f..2bcfb37aca2e5 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_xy/public/components/__snapshots__/xy_chart.test.tsx.snap @@ -305,6 +305,7 @@ exports[`XYChart component it renders area 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -846,6 +847,7 @@ exports[`XYChart component it renders bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -1387,6 +1389,7 @@ exports[`XYChart component it renders horizontal bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -1928,6 +1931,7 @@ exports[`XYChart component it renders line 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -2469,6 +2473,7 @@ exports[`XYChart component it renders stacked area 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -3010,6 +3015,7 @@ exports[`XYChart component it renders stacked bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} @@ -3551,6 +3557,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = ` } } legendPosition="top" + legendSize={130} onBrushEnd={[Function]} onElementClick={[Function]} onPointerUpdate={[Function]} diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx index de67e814d5b78..7df73082dfa14 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.test.tsx @@ -57,6 +57,7 @@ import { } from '../../common/types'; import { DataLayers } from './data_layers'; import { Annotations } from './annotations'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; const onClickValue = jest.fn(); const onSelectRange = jest.fn(); @@ -2377,6 +2378,37 @@ describe('XYChart component', () => { expect(component.find(Settings).prop('legendPosition')).toEqual('top'); }); + it('computes correct legend sizes', () => { + const { args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Settings).prop('legendSize')).toEqual(80); + + component.setProps({ + args: { + ...args, + legend: { ...args.legend, legendSize: LegendSize.AUTO }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toBeUndefined(); + + component.setProps({ + args: { + ...args, + legend: { ...args.legend, legendSize: undefined }, + }, + }); + expect(component.find(Settings).prop('legendSize')).toEqual(130); + }); + test('it should apply the fitting function to all non-bar series', () => { const data: Datatable = createSampleDatatableWithRows([ { a: 1, b: 2, c: 'I', d: 'Foo' }, diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index db653861a337e..6e3f142996949 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -33,6 +33,10 @@ import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; import { ChartsPluginSetup, ChartsPluginStart, useActiveCursor } from '@kbn/charts-plugin/public'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, +} from '@kbn/visualizations-plugin/common/constants'; import type { FilterEvent, BrushEvent, FormatFactory } from '../types'; import type { CommonXYDataLayerConfig, SeriesType, XYChartProps } from '../../common/types'; import { @@ -506,7 +510,7 @@ export function XYChart({ : legend.isVisible } legendPosition={legend?.isInside ? legendInsideParams : legend.position} - legendSize={legend.legendSize} + legendSize={LegendSizeToPixels[legend.legendSize ?? DEFAULT_LEGEND_SIZE]} theme={{ ...chartTheme, barSeriesStyle: { diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index e6a56bd65fcc7..240eb7ccab6fa 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -3,7 +3,16 @@ "version": "kibana", "ui": true, "optionalPlugins": ["visualizations"], - "requiredBundles": ["unifiedSearch", "kibanaUtils", "kibanaReact", "data", "fieldFormats", "discover", "esUiShared"], + "requiredBundles": [ + "unifiedSearch", + "kibanaUtils", + "kibanaReact", + "data", + "fieldFormats", + "discover", + "esUiShared", + "visualizations" + ], "owner": { "name": "Vis Editors", "githubTeam": "kibana-vis-editors" diff --git a/src/plugins/vis_default_editor/public/components/options/legend_size_settings.test.tsx b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.test.tsx new file mode 100644 index 0000000000000..3eeb93e6155df --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.test.tsx @@ -0,0 +1,83 @@ +/* + * 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'; +import { LegendSizeSettings } from './legend_size_settings'; +import { LegendSize, DEFAULT_LEGEND_SIZE } from '@kbn/visualizations-plugin/public'; +import { EuiSuperSelect } from '@elastic/eui'; +import { shallow } from 'enzyme'; + +describe('legend size settings', () => { + it('select is disabled if not vertical legend', () => { + const instance = shallow( + {}} + isVerticalLegend={false} + showAutoOption={true} + /> + ); + + expect(instance.find(EuiSuperSelect).props().disabled).toBeTruthy(); + }); + + it('reflects current setting in select', () => { + const CURRENT_SIZE = LegendSize.SMALL; + + const instance = shallow( + {}} + isVerticalLegend={true} + showAutoOption={true} + /> + ); + + expect(instance.find(EuiSuperSelect).props().valueOfSelected).toBe(CURRENT_SIZE); + }); + + it('allows user to select a new option', () => { + const onSizeChange = jest.fn(); + + const instance = shallow( + + ); + + const onChange = instance.find(EuiSuperSelect).props().onChange; + + onChange(LegendSize.EXTRA_LARGE); + onChange(DEFAULT_LEGEND_SIZE); + + expect(onSizeChange).toHaveBeenNthCalledWith(1, LegendSize.EXTRA_LARGE); + expect(onSizeChange).toHaveBeenNthCalledWith(2, undefined); + }); + + it('hides "auto" option if visualization not using it', () => { + const getOptions = (showAutoOption: boolean) => + shallow( + {}} + isVerticalLegend={true} + showAutoOption={showAutoOption} + /> + ) + .find(EuiSuperSelect) + .props().options; + + const autoOption = expect.objectContaining({ value: LegendSize.AUTO }); + + expect(getOptions(true)).toContainEqual(autoOption); + expect(getOptions(false)).not.toContainEqual(autoOption); + }); +}); diff --git a/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx index 768db7d3dd78e..bbe47295c99e6 100644 --- a/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx +++ b/src/plugins/vis_default_editor/public/components/options/legend_size_settings.tsx @@ -10,27 +10,11 @@ import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFormRow, EuiSuperSelect, EuiToolTip } from '@elastic/eui'; +import { LegendSize, DEFAULT_LEGEND_SIZE } from '@kbn/visualizations-plugin/public'; -enum LegendSizes { - AUTO = '0', - SMALL = '80', - MEDIUM = '130', - LARGE = '180', - EXTRA_LARGE = '230', -} - -const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ +const legendSizeOptions: Array<{ value: LegendSize; inputDisplay: string }> = [ { - value: LegendSizes.AUTO, - inputDisplay: i18n.translate( - 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.auto', - { - defaultMessage: 'Auto', - } - ), - }, - { - value: LegendSizes.SMALL, + value: LegendSize.SMALL, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.small', { @@ -39,7 +23,7 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ), }, { - value: LegendSizes.MEDIUM, + value: LegendSize.MEDIUM, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.medium', { @@ -48,7 +32,7 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ), }, { - value: LegendSizes.LARGE, + value: LegendSize.LARGE, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.large', { @@ -57,7 +41,7 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ), }, { - value: LegendSizes.EXTRA_LARGE, + value: LegendSize.EXTRA_LARGE, inputDisplay: i18n.translate( 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.extraLarge', { @@ -68,15 +52,17 @@ const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ ]; interface LegendSizeSettingsProps { - legendSize?: number; - onLegendSizeChange: (size?: number) => void; + legendSize?: LegendSize; + onLegendSizeChange: (size?: LegendSize) => void; isVerticalLegend: boolean; + showAutoOption: boolean; } export const LegendSizeSettings = ({ legendSize, onLegendSizeChange, isVerticalLegend, + showAutoOption, }: LegendSizeSettingsProps) => { useEffect(() => { if (legendSize && !isVerticalLegend) { @@ -85,16 +71,31 @@ export const LegendSizeSettings = ({ }, [isVerticalLegend, legendSize, onLegendSizeChange]); const onLegendSizeOptionChange = useCallback( - (option) => onLegendSizeChange(Number(option) || undefined), + (option) => onLegendSizeChange(option === DEFAULT_LEGEND_SIZE ? undefined : option), [onLegendSizeChange] ); + const options = showAutoOption + ? [ + { + value: LegendSize.AUTO, + inputDisplay: i18n.translate( + 'visDefaultEditor.options.legendSizeSetting.legendSizeOptions.auto', + { + defaultMessage: 'Auto', + } + ), + }, + ...legendSizeOptions, + ] + : legendSizeOptions; + const legendSizeSelect = ( diff --git a/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx b/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx index f592ee3933c1c..3c06e65e2cff4 100644 --- a/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx +++ b/src/plugins/vis_types/heatmap/public/editor/components/heatmap.tsx @@ -26,7 +26,7 @@ import { LegendSizeSettings, } from '@kbn/vis-default-editor-plugin/public'; import { colorSchemas } from '@kbn/charts-plugin/public'; -import { VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; +import { LegendSize, VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; import { HeatmapVisParams, HeatmapTypeProps, ValueAxis } from '../../types'; import { LabelsPanel } from './labels_panel'; import { legendPositions, scaleTypes } from '../collections'; @@ -42,6 +42,9 @@ const HeatmapOptions = (props: HeatmapOptionsProps) => { const isColorsNumberInvalid = stateParams.colorsNumber < 2 || stateParams.colorsNumber > 10; const [isColorRangesValid, setIsColorRangesValid] = useState(false); + const legendSize = stateParams.legendSize; + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + const setValueAxisScale = useCallback( (paramName: T, value: ValueAxis['scale'][T]) => setValue('valueAxes', [ @@ -91,12 +94,13 @@ const HeatmapOptions = (props: HeatmapOptionsProps) => { setValue={setValue} /> )} diff --git a/src/plugins/vis_types/heatmap/public/types.ts b/src/plugins/vis_types/heatmap/public/types.ts index 8301d246e9f63..9d41a132f00b1 100644 --- a/src/plugins/vis_types/heatmap/public/types.ts +++ b/src/plugins/vis_types/heatmap/public/types.ts @@ -9,6 +9,7 @@ import { UiCounterMetricType } from '@kbn/analytics'; import type { Position } from '@elastic/charts'; import type { ChartsPluginSetup, Style, Labels, ColorSchemas } from '@kbn/charts-plugin/public'; import { Range } from '@kbn/expressions-plugin/public'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; export interface HeatmapTypeProps { showElasticChartsOptions?: boolean; @@ -23,7 +24,7 @@ export interface HeatmapVisParams { legendPosition: Position; truncateLegend?: boolean; maxLegendLines?: number; - legendSize?: number; + legendSize?: LegendSize; lastRangeIsRightOpen: boolean; percentageMode: boolean; valueAxes: ValueAxis[]; diff --git a/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap index 904dff6ee1192..5b8bd613609f9 100644 --- a/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/pie/public/__snapshots__/to_ast.test.ts.snap @@ -93,6 +93,9 @@ Object { "legendPosition": Array [ "right", ], + "legendSize": Array [ + "large", + ], "metric": Array [ Object { "chain": Array [ diff --git a/src/plugins/vis_types/pie/public/editor/components/pie.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.tsx index f1f335f186ffd..cd1e565861d78 100644 --- a/src/plugins/vis_types/pie/public/editor/components/pie.tsx +++ b/src/plugins/vis_types/pie/public/editor/components/pie.tsx @@ -31,7 +31,7 @@ import { LongLegendOptions, LegendSizeSettings, } from '@kbn/vis-default-editor-plugin/public'; -import { VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; +import { LegendSize, VisEditorOptionsProps } from '@kbn/visualizations-plugin/public'; import { PartitionVisParams, LabelPositions, @@ -97,6 +97,9 @@ const PieOptions = (props: PieOptionsProps) => { const hasSplitChart = Boolean(aggs?.aggs?.find((agg) => agg.schema === 'split' && agg.enabled)); const segments = aggs?.aggs?.filter((agg) => agg.schema === 'segment' && agg.enabled) ?? []; + const legendSize = stateParams.legendSize; + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + const getLegendDisplay = useCallback( (isVisible: boolean) => (isVisible ? LegendDisplay.SHOW : LegendDisplay.HIDE), [] @@ -234,12 +237,13 @@ const PieOptions = (props: PieOptionsProps) => { setValue={setValue} /> )} diff --git a/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts index f8836f208d916..4c638689ca310 100644 --- a/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_types/pie/public/sample_vis.test.mocks.ts @@ -7,6 +7,7 @@ */ import { LegendDisplay } from '@kbn/expression-partition-vis-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; export const samplePieVis = { type: { @@ -142,6 +143,7 @@ export const samplePieVis = { addTooltip: true, legendDisplay: LegendDisplay.SHOW, legendPosition: 'right', + legendSize: LegendSize.LARGE, isDonut: true, labels: { show: true, diff --git a/src/plugins/vis_types/pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts index aaac3040d7bd3..7a131dbb76b9c 100644 --- a/src/plugins/vis_types/pie/public/to_ast.ts +++ b/src/plugins/vis_types/pie/public/to_ast.ts @@ -62,14 +62,14 @@ export const toExpressionAst: VisToExpressionAst = async (vi addTooltip: vis.params.addTooltip, legendDisplay: vis.params.legendDisplay, legendPosition: vis.params.legendPosition, - nestedLegend: vis.params?.nestedLegend ?? false, + nestedLegend: vis.params.nestedLegend ?? false, truncateLegend: vis.params.truncateLegend, maxLegendLines: vis.params.maxLegendLines, legendSize: vis.params.legendSize, - distinctColors: vis.params?.distinctColors, + distinctColors: vis.params.distinctColors, isDonut: vis.params.isDonut ?? false, emptySizeRatio: vis.params.emptySizeRatio, - palette: preparePalette(vis.params?.palette), + palette: preparePalette(vis.params.palette), labels: prepareLabels(vis.params.labels), metric: schemas.metric.map(prepareDimension), buckets: schemas.segment?.map(prepareDimension), diff --git a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap index 233940d97d38a..6d20088dbff32 100644 --- a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap @@ -8,7 +8,7 @@ Object { "area", ], "visConfig": Array [ - "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"circlesRadius\\":5,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"truncateLegend\\":true,\\"maxLegendLines\\":1,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", + "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"circlesRadius\\":5,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"legendSize\\":\\"small\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"truncateLegend\\":true,\\"maxLegendLines\\":1,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", ], }, "getArgument": [Function], diff --git a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap index 1eedae99ffedb..80e52d95be5c9 100644 --- a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap +++ b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap @@ -5,7 +5,7 @@ Object { "addArgument": [Function], "arguments": Object { "visConfig": Array [ - "{\\"type\\":\\"pie\\",\\"addTooltip\\":true,\\"legendDisplay\\":\\"show\\",\\"legendPosition\\":\\"right\\",\\"isDonut\\":true,\\"labels\\":{\\"show\\":true,\\"values\\":true,\\"last_level\\":true,\\"truncate\\":100},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{}},\\"buckets\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", + "{\\"type\\":\\"pie\\",\\"addTooltip\\":true,\\"legendDisplay\\":\\"show\\",\\"legendPosition\\":\\"right\\",\\"legendSize\\":\\"large\\",\\"isDonut\\":true,\\"labels\\":{\\"show\\":true,\\"values\\":true,\\"last_level\\":true,\\"truncate\\":100},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{}},\\"buckets\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", ], }, "getArgument": [Function], diff --git a/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap index 7ee1b0d2b2053..048b07dbf34ed 100644 --- a/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap @@ -32,6 +32,9 @@ Object { "legendPosition": Array [ "top", ], + "legendSize": Array [ + "small", + ], "maxLegendLines": Array [ 1, ], diff --git a/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx b/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx index 15b5adf00b41f..c12eae1b20b8e 100644 --- a/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx +++ b/src/plugins/vis_types/xy/public/editor/components/options/point_series/point_series.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; @@ -20,6 +20,7 @@ import { } from '@kbn/vis-default-editor-plugin/public'; import { BUCKET_TYPES } from '@kbn/data-plugin/public'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { VisParams } from '../../../../types'; import { GridPanel } from './grid_panel'; import { ThresholdPanel } from './threshold_panel'; @@ -41,6 +42,10 @@ export function PointSeriesOptions(props: ValidationVisOptionsProps) [stateParams.seriesParams, aggs.aggs] ); + const legendSize = stateParams.legendSize; + + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + const handleLegendSizeChange = useCallback((size) => setValue('legendSize', size), [setValue]); return ( @@ -64,12 +69,13 @@ export function PointSeriesOptions(props: ValidationVisOptionsProps) setValue={setValue} /> {vis.data.aggs!.aggs.some( diff --git a/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts index 96c4ab112caf1..08319e8e9a11b 100644 --- a/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts @@ -13,7 +13,12 @@ import type { Datatable, Render, } from '@kbn/expressions-plugin/common'; -import { prepareLogTable, Dimension } from '@kbn/visualizations-plugin/public'; +import { + prepareLogTable, + Dimension, + DEFAULT_LEGEND_SIZE, + LegendSize, +} from '@kbn/visualizations-plugin/public'; import type { ChartType } from '../../common'; import type { VisParams, XYVisConfig } from '../types'; @@ -73,10 +78,19 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ }), }, legendSize: { - types: ['number'], + types: ['string'], + default: DEFAULT_LEGEND_SIZE, help: i18n.translate('visTypeXy.function.args.args.legendSize.help', { - defaultMessage: 'Specifies the legend size in pixels.', + defaultMessage: 'Specifies the legend size.', }), + options: [ + LegendSize.AUTO, + LegendSize.SMALL, + LegendSize.MEDIUM, + LegendSize.LARGE, + LegendSize.EXTRA_LARGE, + ], + strict: true, }, addLegend: { types: ['boolean'], diff --git a/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts index 436a284b1657a..3c1d87d2efc3c 100644 --- a/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts @@ -5,6 +5,9 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + +import { LegendSize } from '@kbn/visualizations-plugin/common'; + export const sampleAreaVis = { type: { name: 'area', @@ -282,6 +285,7 @@ export const sampleAreaVis = { addTooltip: true, addLegend: true, legendPosition: 'top', + legendSize: LegendSize.SMALL, times: [], addTimeMarker: false, truncateLegend: true, diff --git a/src/plugins/vis_types/xy/public/types/param.ts b/src/plugins/vis_types/xy/public/types/param.ts index 708eb1cbdd196..a491efad97fcb 100644 --- a/src/plugins/vis_types/xy/public/types/param.ts +++ b/src/plugins/vis_types/xy/public/types/param.ts @@ -15,6 +15,7 @@ import type { FakeParams, HistogramParams, DateHistogramParams, + LegendSize, } from '@kbn/visualizations-plugin/public'; import type { ChartType, XyVisType } from '../../common'; import type { @@ -124,7 +125,7 @@ export interface VisParams { addTimeMarker: boolean; truncateLegend: boolean; maxLegendLines: number; - legendSize?: number; + legendSize?: LegendSize; categoryAxes: CategoryAxis[]; orderBucketsBySum?: boolean; labels: Labels; @@ -165,7 +166,7 @@ export interface XYVisConfig { addTimeMarker: boolean; truncateLegend: boolean; maxLegendLines: number; - legendSize?: number; + legendSize?: LegendSize; orderBucketsBySum?: boolean; labels: ExpressionValueLabel; thresholdLine: ExpressionValueThresholdLine; diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index 7c0636ab284fb..a744841601a67 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -33,7 +33,11 @@ import { useActiveCursor, } from '@kbn/charts-plugin/public'; import { Datatable, IInterpreterRenderHandlers } from '@kbn/expressions-plugin/public'; -import type { PersistedState } from '@kbn/visualizations-plugin/public'; +import { + DEFAULT_LEGEND_SIZE, + LegendSizeToPixels, + PersistedState, +} from '@kbn/visualizations-plugin/public'; import { VisParams } from './types'; import { getAdjustedDomain, @@ -361,7 +365,7 @@ const VisComponent = (props: VisComponentProps) => { tooltip: { visible: syncTooltips, placement: Placement.Right }, }} legendPosition={legendPosition} - legendSize={visParams.legendSize} + legendSize={LegendSizeToPixels[visParams.legendSize ?? DEFAULT_LEGEND_SIZE]} xDomain={xDomain} adjustedXDomain={adjustedXDomain} legendColorPicker={legendColorPicker} diff --git a/src/plugins/visualizations/common/constants.ts b/src/plugins/visualizations/common/constants.ts index 0b840c8ff13fc..ea695e6bdca02 100644 --- a/src/plugins/visualizations/common/constants.ts +++ b/src/plugins/visualizations/common/constants.ts @@ -26,3 +26,21 @@ export const VisualizeConstants = { EDIT_BY_VALUE_PATH: '/edit_by_value', APP_ID: 'visualize', }; + +export enum LegendSize { + AUTO = 'auto', + SMALL = 'small', + MEDIUM = 'medium', + LARGE = 'large', + EXTRA_LARGE = 'xlarge', +} + +export const LegendSizeToPixels = { + [LegendSize.AUTO]: undefined, + [LegendSize.SMALL]: 80, + [LegendSize.MEDIUM]: 130, + [LegendSize.LARGE]: 180, + [LegendSize.EXTRA_LARGE]: 230, +} as const; + +export const DEFAULT_LEGEND_SIZE = LegendSize.MEDIUM; diff --git a/src/plugins/visualizations/common/index.ts b/src/plugins/visualizations/common/index.ts index d784fcfd09eb9..1dd9a0e90477c 100644 --- a/src/plugins/visualizations/common/index.ts +++ b/src/plugins/visualizations/common/index.ts @@ -13,3 +13,4 @@ export * from './types'; export * from './utils'; export * from './expression_functions'; +export { LegendSize, LegendSizeToPixels, DEFAULT_LEGEND_SIZE } from './constants'; diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 22217e9de9abe..67b13c8236708 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -56,6 +56,9 @@ export { VISUALIZE_ENABLE_LABS_SETTING, SAVED_OBJECTS_LIMIT_SETTING, SAVED_OBJECTS_PER_PAGE_SETTING, + LegendSize, + LegendSizeToPixels, + DEFAULT_LEGEND_SIZE, } from '../common/constants'; export type { SavedVisState, VisParams, Dimension } from '../common'; export { prepareLogTable } from '../common'; diff --git a/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts b/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts index d92810743bed4..1d8a00ab2e33b 100644 --- a/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts +++ b/src/plugins/visualizations/server/embeddable/make_visualize_embeddable_factory.ts @@ -26,6 +26,7 @@ import { commonAddDropLastBucketIntoTSVBModel714Above, commonRemoveMarkdownLessFromTSVB, commonUpdatePieVisApi, + commonPreserveOldLegendSizeDefault, } from '../migrations/visualization_common_migrations'; import { SerializedVis } from '../../common'; @@ -97,6 +98,11 @@ const byValueUpdatePieVisApi = (state: SerializableRecord) => ({ savedVis: commonUpdatePieVisApi(state.savedVis), }); +const byValuePreserveOldLegendSizeDefault = (state: SerializableRecord) => ({ + ...state, + savedVis: commonPreserveOldLegendSizeDefault(state.savedVis), +}); + const getEmbeddedVisualizationSearchSourceMigrations = ( searchSourceMigrations: MigrateFunctionsObject ) => @@ -144,6 +150,7 @@ export const makeVisualizeEmbeddableFactory = '7.17.0': (state) => flow(byValueAddDropLastBucketIntoTSVBModel714Above)(state), '8.0.0': (state) => flow(byValueRemoveMarkdownLessFromTSVB)(state), '8.1.0': (state) => flow(byValueUpdatePieVisApi)(state), + '8.3.0': (state) => flow(byValuePreserveOldLegendSizeDefault)(state), } ), }; diff --git a/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts b/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts index aec452e356abe..57d8142822882 100644 --- a/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts +++ b/src/plugins/visualizations/server/migrations/visualization_common_migrations.ts @@ -215,3 +215,34 @@ export const commonUpdatePieVisApi = (visState: any) => { return visState; }; + +export const commonPreserveOldLegendSizeDefault = (visState: any) => { + const visualizationTypesWithLegends = [ + 'pie', + 'area', + 'histogram', + 'horizontal_bar', + 'line', + 'heatmap', + ]; + + const pixelsToLegendSize: Record = { + undefined: 'auto', + '80': 'small', + '130': 'medium', + '180': 'large', + '230': 'xlarge', + }; + + if (visualizationTypesWithLegends.includes(visState?.type)) { + return { + ...visState, + params: { + ...visState.params, + legendSize: pixelsToLegendSize[visState.params?.legendSize], + }, + }; + } + + return visState; +}; diff --git a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts index 19f117ec18cc8..626dc14e05396 100644 --- a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts +++ b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.test.ts @@ -2564,4 +2564,63 @@ describe('migration visualization', () => { expect(otherParams.addLegend).toBeUndefined(); }); }); + + describe('8.3.0 - preserves default legend size for existing visualizations', () => { + const getDoc = (type: string, legendSize: number | undefined) => ({ + attributes: { + title: 'Some Vis with a Legend', + description: '', + visState: JSON.stringify({ + type, + title: 'Pie vis', + params: { + legendSize, + }, + }), + }, + }); + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['8.3.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const autoLegendSize = 'auto'; + const largeLegendSize = 'large'; + const largeLegendSizePx = 180; + + test.each([ + ['pie', undefined, autoLegendSize], + ['area', undefined, autoLegendSize], + ['histogram', undefined, autoLegendSize], + ['horizontal_bar', undefined, autoLegendSize], + ['line', undefined, autoLegendSize], + ['heatmap', undefined, autoLegendSize], + ['pie', largeLegendSizePx, largeLegendSize], + ['area', largeLegendSizePx, largeLegendSize], + ['histogram', largeLegendSizePx, largeLegendSize], + ['horizontal_bar', largeLegendSizePx, largeLegendSize], + ['line', largeLegendSizePx, largeLegendSize], + ['heatmap', largeLegendSizePx, largeLegendSize], + ])( + 'given a %s visualization with current legend size of %s -- sets legend size to %s', + ( + visualizationType: string, + currentLegendSize: number | undefined, + expectedLegendSize: string + ) => { + const visState = JSON.parse( + migrate(getDoc(visualizationType, currentLegendSize)).attributes.visState + ); + + expect(visState.params.legendSize).toBe(expectedLegendSize); + } + ); + + test.each(['metric', 'gauge', 'table'])('leaves visualization without legend alone: %s', () => { + const visState = JSON.parse(migrate(getDoc('table', undefined)).attributes.visState); + + expect(visState.params.legendSize).toBeUndefined(); + }); + }); }); diff --git a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts index d236ad83c853a..bb2d68cfd35d9 100644 --- a/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts +++ b/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts @@ -28,6 +28,7 @@ import { commonAddDropLastBucketIntoTSVBModel714Above, commonRemoveMarkdownLessFromTSVB, commonUpdatePieVisApi, + commonPreserveOldLegendSizeDefault, } from './visualization_common_migrations'; import { VisualizationSavedObjectAttributes } from '../../common'; @@ -1158,6 +1159,30 @@ export const updatePieVisApi: SavedObjectMigrationFn = (doc) => { return doc; }; +const preserveOldLegendSizeDefault: SavedObjectMigrationFn = (doc) => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + const newVisState = commonPreserveOldLegendSizeDefault(visState); + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(newVisState), + }, + }; + } + + return doc; +}; + const visualizationSavedObjectTypeMigrations = { /** * We need to have this migration twice, once with a version prior to 7.0.0 once with a version @@ -1214,6 +1239,7 @@ const visualizationSavedObjectTypeMigrations = { '7.17.0': flow(addDropLastBucketIntoTSVBModel714Above), '8.0.0': flow(removeMarkdownLessFromTSVB), '8.1.0': flow(updatePieVisApi), + '8.3.0': preserveOldLegendSizeDefault, }; /** diff --git a/test/functional/apps/dashboard/group2/dashboard_snapshots.ts b/test/functional/apps/dashboard/group2/dashboard_snapshots.ts index dc1a74ea74b7d..56dcfe2388bc2 100644 --- a/test/functional/apps/dashboard/group2/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/group2/dashboard_snapshots.ts @@ -84,7 +84,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.022); + expect(percentDifference).to.be.lessThan(0.029); }); }); } diff --git a/test/functional/screenshots/baseline/area_chart.png b/test/functional/screenshots/baseline/area_chart.png index 851f53499e94f..7bbaa256f0360 100644 Binary files a/test/functional/screenshots/baseline/area_chart.png and b/test/functional/screenshots/baseline/area_chart.png differ diff --git a/x-pack/plugins/lens/common/types.ts b/x-pack/plugins/lens/common/types.ts index f85c8ba88a076..43f4afb9e0484 100644 --- a/x-pack/plugins/lens/common/types.ts +++ b/x-pack/plugins/lens/common/types.ts @@ -12,6 +12,7 @@ import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import type { Datatable } from '@kbn/expressions-plugin/common'; import type { ColorMode } from '@kbn/charts-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/common'; import { CategoryDisplay, layerTypes, @@ -83,7 +84,7 @@ export interface SharedPieLayerState { percentDecimals?: number; emptySizeRatio?: number; legendMaxLines?: number; - legendSize?: number; + legendSize?: LegendSize; truncateLegend?: boolean; } diff --git a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx index a8d94b743adf8..96f4ef7daf89b 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/toolbar_component.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import React, { memo } from 'react'; +import React, { memo, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, IconType } from '@elastic/eui'; import { Position } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import type { VisualizationToolbarProps } from '../types'; import { LegendSettingsPopover, @@ -49,6 +50,11 @@ export const HeatmapToolbar = memo( state, frame.datasourceLayers ).truncateText; + + const legendSize = state?.legend.legendSize; + + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + return ( @@ -112,16 +118,17 @@ export const HeatmapToolbar = memo( legend: { ...state.legend, shouldTruncate: !current }, }); }} - legendSize={state?.legend.legendSize} - onLegendSizeChange={(legendSize) => { + legendSize={legendSize} + onLegendSizeChange={(newLegendSize) => { setState({ ...state, legend: { ...state.legend, - legendSize, + legendSize: newLegendSize, }, }); }} + showAutoLegendSizeOption={hadAutoLegendSize} /> diff --git a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx index 23b484f4cfd13..f0a8e65889e8e 100644 --- a/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/heatmap_visualization/visualization.tsx @@ -304,6 +304,7 @@ export const getHeatmapVisualization = ({ if (!originalOrder || !state.valueAccessor) { return null; } + return { type: 'expression', chain: [ diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index 9f506c7beb878..73ddeea627967 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -134,20 +134,22 @@ const generateCommonArguments: GenerateExpressionAstArguments = ( layer, datasourceLayers, paletteService -) => ({ - labels: generateCommonLabelsAstArgs(state, attributes, layer), - buckets: operations.map((o) => o.columnId).map(prepareDimension), - metric: layer.metric ? [prepareDimension(layer.metric)] : [], - legendDisplay: [attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay], - legendPosition: [layer.legendPosition || Position.Right], - maxLegendLines: [layer.legendMaxLines ?? 1], - legendSize: layer.legendSize ? [layer.legendSize] : [], - nestedLegend: [!!layer.nestedLegend], - truncateLegend: [ - layer.truncateLegend ?? getDefaultVisualValuesForLayer(state, datasourceLayers).truncateText, - ], - palette: generatePaletteAstArguments(paletteService, state.palette), -}); +) => { + return { + labels: generateCommonLabelsAstArgs(state, attributes, layer), + buckets: operations.map((o) => o.columnId).map(prepareDimension), + metric: layer.metric ? [prepareDimension(layer.metric)] : [], + legendDisplay: [attributes.isPreview ? LegendDisplay.HIDE : layer.legendDisplay], + legendPosition: [layer.legendPosition || Position.Right], + maxLegendLines: [layer.legendMaxLines ?? 1], + legendSize: layer.legendSize ? [layer.legendSize] : [], + nestedLegend: [!!layer.nestedLegend], + truncateLegend: [ + layer.truncateLegend ?? getDefaultVisualValuesForLayer(state, datasourceLayers).truncateText, + ], + palette: generatePaletteAstArguments(paletteService, state.palette), + }; +}; const generatePieVisAst: GenerateExpressionAstFunction = (...rest) => ({ type: 'expression', diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx index 67659d91ecefe..fefa01d708dc7 100644 --- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx @@ -6,7 +6,7 @@ */ import './toolbar.scss'; -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import type { Position } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { DEFAULT_PERCENT_DECIMALS } from './constants'; import { PartitionChartsMeta } from './partition_charts_meta'; import { LegendDisplay, PieVisualizationState, SharedPieLayerState } from '../../common'; @@ -73,6 +74,10 @@ export function PieToolbar(props: VisualizationToolbarProps legendSize === LegendSize.AUTO); + const onStateChange = useCallback( (part: Record) => { setState({ @@ -259,8 +264,9 @@ export function PieToolbar(props: VisualizationToolbarProps ); diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx index 9bf9a1885e6ac..777f2860cb8b6 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.test.tsx @@ -36,6 +36,7 @@ describe('Legend Settings', () => { props = { legendOptions, mode: 'auto', + showAutoLegendSizeOption: true, onDisplayChange: jest.fn(), onPositionChange: jest.fn(), onLegendSizeChange: jest.fn(), diff --git a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx index 944c55fb56091..e0dd990f0a99e 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_settings_popover.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import { Position, VerticalAlignment, HorizontalAlignment } from '@elastic/charts'; import { ToolbarButtonProps } from '@kbn/kibana-react-plugin/public'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import { ToolbarPopover } from '.'; import { LegendLocationSettings } from './legend_location_settings'; import { ColumnsNumberSetting } from './columns_number_setting'; @@ -122,11 +123,16 @@ export interface LegendSettingsPopoverProps { /** * Legend size in pixels */ - legendSize?: number; + legendSize?: LegendSize; /** * Callback on legend size change */ - onLegendSizeChange: (size?: number) => void; + onLegendSizeChange: (size?: LegendSize) => void; + /** + * Whether to show auto legend size option. Should only be true for pre 8.3 visualizations that already had it as their setting. + * (We're trying to get people to stop using it so it can eventually be removed.) + */ + showAutoLegendSizeOption: boolean; } const DEFAULT_TRUNCATE_LINES = 1; @@ -185,6 +191,7 @@ export const LegendSettingsPopover: React.FunctionComponent {}, legendSize, onLegendSizeChange, + showAutoLegendSizeOption, }) => { return ( )} {location && ( diff --git a/x-pack/plugins/lens/public/shared_components/legend_size_settings.test.tsx b/x-pack/plugins/lens/public/shared_components/legend_size_settings.test.tsx new file mode 100644 index 0000000000000..9d5fa2cd8794c --- /dev/null +++ b/x-pack/plugins/lens/public/shared_components/legend_size_settings.test.tsx @@ -0,0 +1,97 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { LegendSizeSettings } from './legend_size_settings'; +import { EuiSuperSelect } from '@elastic/eui'; +import { shallow } from 'enzyme'; +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/public'; + +describe('legend size settings', () => { + it('renders nothing if not vertical legend', () => { + const instance = shallow( + {}} + isVerticalLegend={false} + showAutoOption={false} + /> + ); + + expect(instance.html()).toBeNull(); + }); + + it('defaults to correct value', () => { + const instance = shallow( + {}} + isVerticalLegend={true} + showAutoOption={false} + /> + ); + + expect(instance.find(EuiSuperSelect).props().valueOfSelected).toBe( + DEFAULT_LEGEND_SIZE.toString() + ); + }); + + it('reflects current setting in select', () => { + const CURRENT_SIZE = LegendSize.SMALL; + + const instance = shallow( + {}} + isVerticalLegend={true} + showAutoOption={false} + /> + ); + + expect(instance.find(EuiSuperSelect).props().valueOfSelected).toBe(CURRENT_SIZE); + }); + + it('allows user to select a new option', () => { + const onSizeChange = jest.fn(); + + const instance = shallow( + + ); + + const onChange = instance.find(EuiSuperSelect).props().onChange; + + onChange(LegendSize.EXTRA_LARGE); + onChange(DEFAULT_LEGEND_SIZE); + + expect(onSizeChange).toHaveBeenNthCalledWith(1, LegendSize.EXTRA_LARGE); + expect(onSizeChange).toHaveBeenNthCalledWith(2, undefined); + }); + + it('hides "auto" option if visualization not using it', () => { + const getOptions = (showAutoOption: boolean) => + shallow( + {}} + isVerticalLegend={true} + showAutoOption={showAutoOption} + /> + ) + .find(EuiSuperSelect) + .props().options; + + const autoOption = expect.objectContaining({ value: LegendSize.AUTO }); + + expect(getOptions(true)).toContainEqual(autoOption); + expect(getOptions(false)).not.toContainEqual(autoOption); + }); +}); diff --git a/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx b/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx index 53da283de0b68..15e74f2601b2b 100644 --- a/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx +++ b/x-pack/plugins/lens/public/shared_components/legend_size_settings.tsx @@ -8,48 +8,36 @@ import React, { useEffect, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSuperSelect } from '@elastic/eui'; - -export enum LegendSizes { - AUTO = '0', - SMALL = '80', - MEDIUM = '130', - LARGE = '180', - EXTRA_LARGE = '230', -} +import { DEFAULT_LEGEND_SIZE, LegendSize } from '@kbn/visualizations-plugin/public'; interface LegendSizeSettingsProps { - legendSize: number | undefined; - onLegendSizeChange: (size?: number) => void; + legendSize?: LegendSize; + onLegendSizeChange: (size?: LegendSize) => void; isVerticalLegend: boolean; + showAutoOption: boolean; } -const legendSizeOptions: Array<{ value: LegendSizes; inputDisplay: string }> = [ - { - value: LegendSizes.AUTO, - inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.auto', { - defaultMessage: 'Auto', - }), - }, +const legendSizeOptions: Array<{ value: LegendSize; inputDisplay: string }> = [ { - value: LegendSizes.SMALL, + value: LegendSize.SMALL, inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.small', { defaultMessage: 'Small', }), }, { - value: LegendSizes.MEDIUM, + value: LegendSize.MEDIUM, inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.medium', { defaultMessage: 'Medium', }), }, { - value: LegendSizes.LARGE, + value: LegendSize.LARGE, inputDisplay: i18n.translate('xpack.lens.shared.legendSizeSetting.legendSizeOptions.large', { defaultMessage: 'Large', }), }, { - value: LegendSizes.EXTRA_LARGE, + value: LegendSize.EXTRA_LARGE, inputDisplay: i18n.translate( 'xpack.lens.shared.legendSizeSetting.legendSizeOptions.extraLarge', { @@ -63,6 +51,7 @@ export const LegendSizeSettings = ({ legendSize, onLegendSizeChange, isVerticalLegend, + showAutoOption, }: LegendSizeSettingsProps) => { useEffect(() => { if (legendSize && !isVerticalLegend) { @@ -71,12 +60,27 @@ export const LegendSizeSettings = ({ }, [isVerticalLegend, legendSize, onLegendSizeChange]); const onLegendSizeOptionChange = useCallback( - (option) => onLegendSizeChange(Number(option) || undefined), + (option: LegendSize) => onLegendSizeChange(option === DEFAULT_LEGEND_SIZE ? undefined : option), [onLegendSizeChange] ); if (!isVerticalLegend) return null; + const options = showAutoOption + ? [ + { + value: LegendSize.AUTO, + inputDisplay: i18n.translate( + 'xpack.lens.shared.legendSizeSetting.legendSizeOptions.auto', + { + defaultMessage: 'Auto', + } + ), + }, + ...legendSizeOptions, + ] + : legendSizeOptions; + return ( diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index 1c73c455dfe9e..1acc53a9db512 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -10,7 +10,8 @@ import { ScaleType } from '@elastic/charts'; import type { PaletteRegistry } from '@kbn/coloring'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; -import type { AxisExtentConfig, ExtendedYConfig, YConfig } from '@kbn/expression-xy-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/common/constants'; +import type { AxisExtentConfig, YConfig, ExtendedYConfig } from '@kbn/expression-xy-plugin/common'; import type { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import { State, @@ -214,10 +215,11 @@ export const buildExpression = ( : [], position: !state.legend.isInside ? [state.legend.position] : [], isInside: state.legend.isInside ? [state.legend.isInside] : [], - legendSize: - !state.legend.isInside && state.legend.legendSize - ? [state.legend.legendSize] - : [], + legendSize: state.legend.isInside + ? [LegendSize.AUTO] + : state.legend.legendSize + ? [state.legend.legendSize] + : [], horizontalAlignment: state.legend.horizontalAlignment && state.legend.isInside ? [state.legend.horizontalAlignment] diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx index 00c4e9c8eaeb2..bd39a61b08acd 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/index.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import React, { memo, useCallback } from 'react'; +import React, { memo, useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { Position, ScaleType } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { AxesSettingsConfig, AxisExtentConfig } from '@kbn/expression-xy-plugin/common'; +import { LegendSize } from '@kbn/visualizations-plugin/public'; import type { VisualizationToolbarProps, FramePublicAPI } from '../../types'; import { State, XYState } from '../types'; import { isHorizontalChart } from '../state_helpers'; @@ -294,6 +295,10 @@ export const XyToolbar = memo(function XyToolbar( props.frame.datasourceLayers ).truncateText; + const legendSize = state.legend.legendSize; + + const [hadAutoLegendSize] = useState(() => legendSize === LegendSize.AUTO); + return ( @@ -398,16 +403,17 @@ export const XyToolbar = memo(function XyToolbar( valuesInLegend: !state.valuesInLegend, }); }} - legendSize={state.legend.legendSize} - onLegendSizeChange={(legendSize) => { + legendSize={legendSize} + onLegendSizeChange={(newLegendSize) => { setState({ ...state, legend: { ...state.legend, - legendSize, + legendSize: newLegendSize, }, }); }} + showAutoLegendSizeOption={hadAutoLegendSize} /> diff --git a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts index 215f080d3dbdf..dc3933d852979 100644 --- a/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts +++ b/x-pack/plugins/lens/server/embeddable/make_lens_embeddable_factory.ts @@ -14,6 +14,7 @@ import { import { DOC_TYPE } from '../../common'; import { commonEnhanceTableRowHeight, + commonPreserveOldLegendSizeDefault, commonFixValueLabelsInXY, commonLockOldMetricVisSettings, commonMakeReversePaletteAsCustom, @@ -35,7 +36,6 @@ import { LensDocShapePre712, VisState716, VisState810, - VisState820, VisStatePre715, VisStatePre830, } from '../migrations/types'; @@ -113,8 +113,9 @@ export const makeLensEmbeddableFactory = } as unknown as SerializableRecord; }, '8.3.0': (state) => { - const lensState = state as unknown as { attributes: LensDocShape810 }; + const lensState = state as unknown as { attributes: LensDocShape810 }; let migratedLensState = commonLockOldMetricVisSettings(lensState.attributes); + migratedLensState = commonPreserveOldLegendSizeDefault(migratedLensState); migratedLensState = commonFixValueLabelsInXY( migratedLensState as LensDocShape810 ); diff --git a/x-pack/plugins/lens/server/migrations/common_migrations.ts b/x-pack/plugins/lens/server/migrations/common_migrations.ts index 7cafa41f569d4..10fc9b25e6f34 100644 --- a/x-pack/plugins/lens/server/migrations/common_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/common_migrations.ts @@ -255,6 +255,38 @@ export const commonLockOldMetricVisSettings = ( return newAttributes as LensDocShape830; }; +export const commonPreserveOldLegendSizeDefault = ( + attributes: LensDocShape810 +): LensDocShape830 => { + const newAttributes = cloneDeep(attributes); + + const pixelsToLegendSize: Record = { + undefined: 'auto', + '80': 'small', + '130': 'medium', + '180': 'large', + '230': 'xlarge', + }; + + if (['lnsXY', 'lnsHeatmap'].includes(newAttributes.visualizationType + '')) { + const legendConfig = (newAttributes.state.visualization as { legend: { legendSize: number } }) + .legend; + (legendConfig.legendSize as unknown as string) = + pixelsToLegendSize[String(legendConfig.legendSize)]; + } + + if (newAttributes.visualizationType === 'lnsPie') { + const layers = (newAttributes.state.visualization as { layers: Array<{ legendSize: number }> }) + .layers; + + layers.forEach((layer) => { + (layer.legendSize as unknown as string) = pixelsToLegendSize[String(layer.legendSize)]; + }); + } + + return newAttributes as LensDocShape830; +}; + const getApplyCustomVisualizationMigrationToLens = (id: string, migration: MigrateFunction) => { return (savedObject: { attributes: LensDocShape }) => { if (savedObject.attributes.visualizationType !== id) return savedObject; diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts index af68c5020e420..d43d4c4cb2a38 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.test.ts @@ -2065,6 +2065,7 @@ describe('Lens migrations', () => { expect(layer2Columns['4'].params).toHaveProperty('includeEmptyRows', true); }); }); + describe('8.3.0 old metric visualization defaults', () => { const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; const example = { @@ -2115,6 +2116,74 @@ describe('Lens migrations', () => { }); }); + describe('8.3.0 - convert legend sizes to strings', () => { + const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; + const migrate = migrations['8.3.0']; + + const autoLegendSize = 'auto'; + const largeLegendSize = 'large'; + const largeLegendSizePx = 180; + + it('works for XY visualization and heatmap', () => { + const getDoc = (type: string, legendSize: number | undefined) => + ({ + attributes: { + visualizationType: type, + state: { + visualization: { + legend: { + legendSize, + }, + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc); + + expect( + migrate(getDoc('lnsXY', undefined), context).attributes.state.visualization.legend + .legendSize + ).toBe(autoLegendSize); + expect( + migrate(getDoc('lnsXY', largeLegendSizePx), context).attributes.state.visualization.legend + .legendSize + ).toBe(largeLegendSize); + + expect( + migrate(getDoc('lnsHeatmap', undefined), context).attributes.state.visualization.legend + .legendSize + ).toBe(autoLegendSize); + expect( + migrate(getDoc('lnsHeatmap', largeLegendSizePx), context).attributes.state.visualization + .legend.legendSize + ).toBe(largeLegendSize); + }); + + it('works for pie visualization', () => { + const pieVisDoc = { + attributes: { + visualizationType: 'lnsPie', + state: { + visualization: { + layers: [ + { + legendSize: undefined, + }, + { + legendSize: largeLegendSizePx, + }, + ], + }, + }, + }, + } as unknown as SavedObjectUnsanitizedDoc; + + expect(migrate(pieVisDoc, context).attributes.state.visualization.layers).toEqual([ + { legendSize: autoLegendSize }, + { legendSize: largeLegendSize }, + ]); + }); + }); + describe('8.3.0 valueLabels in XY', () => { const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; const example = { @@ -2128,6 +2197,7 @@ describe('Lens migrations', () => { state: { visualization: { valueLabels: 'inside', + legend: {}, }, }, }, diff --git a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts index 00ec6c29154e3..3870bab9fad65 100644 --- a/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts +++ b/x-pack/plugins/lens/server/migrations/saved_object_migrations.ts @@ -49,6 +49,7 @@ import { commonSetIncludeEmptyRowsDateHistogram, commonFixValueLabelsInXY, commonLockOldMetricVisSettings, + commonPreserveOldLegendSizeDefault, } from './common_migrations'; interface LensDocShapePre710 { @@ -505,6 +506,10 @@ const lockOldMetricVisSettings: SavedObjectMigrationFn ({ ...doc, attributes: commonLockOldMetricVisSettings(doc.attributes) }); +const preserveOldLegendSizeDefault: SavedObjectMigrationFn = ( + doc +) => ({ ...doc, attributes: commonPreserveOldLegendSizeDefault(doc.attributes) }); + const lensMigrations: SavedObjectMigrationMap = { '7.7.0': removeInvalidAccessors, // The order of these migrations matter, since the timefield migration relies on the aggConfigs @@ -524,7 +529,7 @@ const lensMigrations: SavedObjectMigrationMap = { setIncludeEmptyRowsDateHistogram, enhanceTableRowHeight ), - '8.3.0': flow(lockOldMetricVisSettings, fixValueLabelsInXY), + '8.3.0': flow(lockOldMetricVisSettings, preserveOldLegendSizeDefault, fixValueLabelsInXY), }; export const getAllMigrations = (