diff --git a/src/plugins/vis_default_editor/public/components/options/index.ts b/src/plugins/vis_default_editor/public/components/options/index.ts index 31b09977f5c99..62ce76014f9fc 100644 --- a/src/plugins/vis_default_editor/public/components/options/index.ts +++ b/src/plugins/vis_default_editor/public/components/options/index.ts @@ -16,3 +16,4 @@ export { RangeOption } from './range'; export { RequiredNumberInputOption } from './required_number_input'; export { TextInputOption } from './text_input'; export { PercentageModeOption } from './percentage_mode'; +export { LongLegendOptions } from './long_legend_options'; diff --git a/src/plugins/vis_default_editor/public/components/options/long_legend_options.test.tsx b/src/plugins/vis_default_editor/public/components/options/long_legend_options.test.tsx new file mode 100644 index 0000000000000..69994bb279278 --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/options/long_legend_options.test.tsx @@ -0,0 +1,48 @@ +/* + * 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 { mountWithIntl } from '@kbn/test/jest'; +import { LongLegendOptions, LongLegendOptionsProps } from './long_legend_options'; +import { EuiFieldNumber } from '@elastic/eui'; + +describe('LongLegendOptions', () => { + let props: LongLegendOptionsProps; + let component; + beforeAll(() => { + props = { + truncateLegend: true, + setValue: jest.fn(), + }; + }); + + it('renders the EuiFieldNumber', () => { + component = mountWithIntl(); + expect(component.find(EuiFieldNumber).length).toBe(1); + }); + + it('should call setValue when value is changes in the number input', () => { + component = mountWithIntl(); + const numberField = component.find(EuiFieldNumber); + numberField.props().onChange!(({ + target: { + value: 3, + }, + } as unknown) as React.ChangeEvent); + + expect(props.setValue).toHaveBeenCalledWith('maxLegendLines', 3); + }); + + it('input number should be disabled when truncate is false', () => { + props.truncateLegend = false; + component = mountWithIntl(); + const numberField = component.find(EuiFieldNumber); + + expect(numberField.props().disabled).toBeTruthy(); + }); +}); diff --git a/src/plugins/vis_default_editor/public/components/options/long_legend_options.tsx b/src/plugins/vis_default_editor/public/components/options/long_legend_options.tsx new file mode 100644 index 0000000000000..80ea92a0b0540 --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/options/long_legend_options.tsx @@ -0,0 +1,64 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; +import { SwitchOption } from './switch'; + +export interface LongLegendOptionsProps { + setValue: (paramName: 'maxLegendLines' | 'truncateLegend', value: boolean | number) => void; + truncateLegend: boolean; + maxLegendLines?: number; + 'data-test-subj'?: string; +} + +function LongLegendOptions({ + 'data-test-subj': dataTestSubj, + setValue, + truncateLegend, + maxLegendLines, +}: LongLegendOptionsProps) { + return ( + <> + + + } + > + { + setValue('maxLegendLines', Number(e.target.value)); + }} + /> + + + ); +} + +export { LongLegendOptions }; diff --git a/src/plugins/vis_type_pie/public/__snapshots__/pie_fn.test.ts.snap b/src/plugins/vis_type_pie/public/__snapshots__/pie_fn.test.ts.snap index 6c43072b97c28..fb51717d1adc0 100644 --- a/src/plugins/vis_type_pie/public/__snapshots__/pie_fn.test.ts.snap +++ b/src/plugins/vis_type_pie/public/__snapshots__/pie_fn.test.ts.snap @@ -57,6 +57,7 @@ Object { "valuesFormat": "percent", }, "legendPosition": "right", + "maxLegendLines": true, "metric": Object { "accessor": 0, "aggType": "count", @@ -72,6 +73,7 @@ Object { }, "splitColumn": undefined, "splitRow": undefined, + "truncateLegend": true, }, "visData": Object { "columns": Array [ diff --git a/src/plugins/vis_type_pie/public/editor/components/pie.test.tsx b/src/plugins/vis_type_pie/public/editor/components/pie.test.tsx index 524986524fd7e..d37f4c10ea9ea 100644 --- a/src/plugins/vis_type_pie/public/editor/components/pie.test.tsx +++ b/src/plugins/vis_type_pie/public/editor/components/pie.test.tsx @@ -73,6 +73,20 @@ describe('PalettePicker', function () { }); }); + it('renders the long legend options for the elastic charts implementation', async () => { + component = mountWithIntl(); + await act(async () => { + expect(findTestSubject(component, 'pieLongLegendsOptions').length).toBe(1); + }); + }); + + it('not renders the long legend options for the vislib implementation', async () => { + component = mountWithIntl(); + await act(async () => { + expect(findTestSubject(component, 'pieLongLegendsOptions').length).toBe(0); + }); + }); + it('renders the label position dropdown for the elastic charts implementation', async () => { component = mountWithIntl(); await act(async () => { diff --git a/src/plugins/vis_type_pie/public/editor/components/pie.tsx b/src/plugins/vis_type_pie/public/editor/components/pie.tsx index 8ce4f4defbaed..2e64874d1b57c 100644 --- a/src/plugins/vis_type_pie/public/editor/components/pie.tsx +++ b/src/plugins/vis_type_pie/public/editor/components/pie.tsx @@ -26,6 +26,7 @@ import { SwitchOption, SelectOption, PalettePicker, + LongLegendOptions, } from '../../../../vis_default_editor/public'; import { VisEditorOptionsProps } from '../../../../visualizations/public'; import { TruncateLabelsOption } from './truncate_labels'; @@ -169,6 +170,12 @@ const PieOptions = (props: PieOptionsProps) => { }} data-test-subj="visTypePieNestedLegendSwitch" /> + )} {props.showElasticChartsOptions && palettesRegistry && ( diff --git a/src/plugins/vis_type_pie/public/mocks.ts b/src/plugins/vis_type_pie/public/mocks.ts index 53579422e44eb..6cf291b8bf370 100644 --- a/src/plugins/vis_type_pie/public/mocks.ts +++ b/src/plugins/vis_type_pie/public/mocks.ts @@ -279,6 +279,8 @@ export const createMockPieParams = (): PieVisParams => { }, legendPosition: 'right', nestedLegend: false, + maxLegendLines: 1, + truncateLegend: true, distinctColors: false, palette: { name: 'default', diff --git a/src/plugins/vis_type_pie/public/pie_component.tsx b/src/plugins/vis_type_pie/public/pie_component.tsx index c0f4a8a6112f8..9119f2f2ecd6c 100644 --- a/src/plugins/vis_type_pie/public/pie_component.tsx +++ b/src/plugins/vis_type_pie/public/pie_component.tsx @@ -320,7 +320,16 @@ const PieComponent = (props: PieComponentProps) => { services.actions, services.fieldFormats )} - theme={chartTheme} + theme={[ + chartTheme, + { + legend: { + labelOptions: { + maxLines: visParams.truncateLegend ? visParams.maxLegendLines ?? 1 : 0, + }, + }, + }, + ]} baseTheme={chartBaseTheme} onRenderChange={onRenderChange} /> diff --git a/src/plugins/vis_type_pie/public/pie_fn.test.ts b/src/plugins/vis_type_pie/public/pie_fn.test.ts index 3dcef406379c2..33b5f38cbe630 100644 --- a/src/plugins/vis_type_pie/public/pie_fn.test.ts +++ b/src/plugins/vis_type_pie/public/pie_fn.test.ts @@ -23,6 +23,8 @@ describe('interpreter/functions#pie', () => { legendPosition: 'right', isDonut: true, nestedLegend: true, + truncateLegend: true, + maxLegendLines: true, distinctColors: false, palette: 'kibana_palette', labels: { diff --git a/src/plugins/vis_type_pie/public/pie_fn.ts b/src/plugins/vis_type_pie/public/pie_fn.ts index 65ac648ca2868..c5987001d4494 100644 --- a/src/plugins/vis_type_pie/public/pie_fn.ts +++ b/src/plugins/vis_type_pie/public/pie_fn.ts @@ -89,6 +89,19 @@ export const createPieVisFn = (): VisTypePieExpressionFunctionDefinition => ({ }), default: false, }, + truncateLegend: { + types: ['boolean'], + help: i18n.translate('visTypePie.function.args.truncateLegendHelpText', { + defaultMessage: 'Defines if the legend items will be truncated or not', + }), + default: true, + }, + maxLegendLines: { + types: ['number'], + help: i18n.translate('visTypePie.function.args.maxLegendLinesHelpText', { + defaultMessage: 'Defines the number of lines per legend item', + }), + }, distinctColors: { types: ['boolean'], help: i18n.translate('visTypePie.function.args.distinctColorsHelpText', { diff --git a/src/plugins/vis_type_pie/public/sample_vis.test.mocks.ts b/src/plugins/vis_type_pie/public/sample_vis.test.mocks.ts index 3b07743e79f45..7e0bcdfd9b5c9 100644 --- a/src/plugins/vis_type_pie/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_type_pie/public/sample_vis.test.mocks.ts @@ -28,6 +28,8 @@ export const samplePieVis = { legendPosition: 'right', isDonut: true, nestedLegend: true, + truncateLegend: true, + maxLegendLines: 1, distinctColors: false, palette: 'kibana_palette', labels: { diff --git a/src/plugins/vis_type_pie/public/to_ast.ts b/src/plugins/vis_type_pie/public/to_ast.ts index e8c9f301b4366..b360e375bf40d 100644 --- a/src/plugins/vis_type_pie/public/to_ast.ts +++ b/src/plugins/vis_type_pie/public/to_ast.ts @@ -50,6 +50,8 @@ export const toExpressionAst: VisToExpressionAst = async (vis, par addLegend: vis.params.addLegend, legendPosition: vis.params.legendPosition, nestedLegend: vis.params?.nestedLegend, + truncateLegend: vis.params.truncateLegend, + maxLegendLines: vis.params.maxLegendLines, distinctColors: vis.params?.distinctColors, isDonut: vis.params.isDonut, palette: vis.params?.palette?.name, diff --git a/src/plugins/vis_type_pie/public/types/types.ts b/src/plugins/vis_type_pie/public/types/types.ts index 4f3365545d062..94eaeb55f7242 100644 --- a/src/plugins/vis_type_pie/public/types/types.ts +++ b/src/plugins/vis_type_pie/public/types/types.ts @@ -33,6 +33,8 @@ interface PieCommonParams { addLegend: boolean; legendPosition: Position; nestedLegend: boolean; + truncateLegend: boolean; + maxLegendLines: number; distinctColors: boolean; isDonut: boolean; } diff --git a/src/plugins/vis_type_pie/public/utils/get_columns.test.ts b/src/plugins/vis_type_pie/public/utils/get_columns.test.ts index 3170628ec2e12..9f64266ed0e0e 100644 --- a/src/plugins/vis_type_pie/public/utils/get_columns.test.ts +++ b/src/plugins/vis_type_pie/public/utils/get_columns.test.ts @@ -144,6 +144,8 @@ describe('getColumns', () => { }, legendPosition: 'right', nestedLegend: false, + maxLegendLines: 1, + truncateLegend: false, palette: { name: 'default', type: 'palette', diff --git a/src/plugins/vis_type_pie/public/vis_type/pie.ts b/src/plugins/vis_type_pie/public/vis_type/pie.ts index 9d1556ac33ad7..95a9d0d41481b 100644 --- a/src/plugins/vis_type_pie/public/vis_type/pie.ts +++ b/src/plugins/vis_type_pie/public/vis_type/pie.ts @@ -35,6 +35,8 @@ export const getPieVisTypeDefinition = ({ addLegend: !showElasticChartsOptions, legendPosition: Position.Right, nestedLegend: false, + truncateLegend: true, + maxLegendLines: 1, distinctColors: false, isDonut: true, palette: { diff --git a/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap index 7c21e699216bc..7ee1b0d2b2053 100644 --- a/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_type_xy/public/__snapshots__/to_ast.test.ts.snap @@ -32,6 +32,9 @@ Object { "legendPosition": Array [ "top", ], + "maxLegendLines": Array [ + 1, + ], "palette": Array [ "default", ], @@ -51,6 +54,9 @@ Object { }, ], "times": Array [], + "truncateLegend": Array [ + true, + ], "type": Array [ "area", ], diff --git a/src/plugins/vis_type_xy/public/components/xy_settings.tsx b/src/plugins/vis_type_xy/public/components/xy_settings.tsx index 03455bae69506..2dd7d7e0a91f9 100644 --- a/src/plugins/vis_type_xy/public/components/xy_settings.tsx +++ b/src/plugins/vis_type_xy/public/components/xy_settings.tsx @@ -60,6 +60,8 @@ type XYSettingsProps = Pick< legendAction?: LegendAction; legendColorPicker: LegendColorPicker; legendPosition: Position; + truncateLegend: boolean; + maxLegendLines: number; }; function getValueLabelsStyling() { @@ -93,6 +95,8 @@ export const XYSettings: FC = ({ legendAction, legendColorPicker, legendPosition, + maxLegendLines, + truncateLegend, }) => { const themeService = getThemeService(); const theme = themeService.useChartsTheme(); @@ -113,6 +117,9 @@ export const XYSettings: FC = ({ crosshair: { ...theme.crosshair, }, + legend: { + labelOptions: { maxLines: truncateLegend ? maxLegendLines ?? 1 : 0 }, + }, axes: { axisTitle: { padding: { diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.mocks.ts b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.mocks.ts index f23d9e4ada336..823fc54f6c92f 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.mocks.ts +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.mocks.ts @@ -426,6 +426,8 @@ export const getVis = (bucketType: string) => { fittingFunction: 'linear', times: [], addTimeMarker: false, + maxLegendLines: 1, + truncateLegend: true, radiusRatio: 9, thresholdLine: { show: false, @@ -849,6 +851,8 @@ export const getStateParams = (type: string, thresholdPanelOn: boolean) => { legendPosition: 'right', times: [], addTimeMarker: false, + maxLegendLines: 1, + truncateLegend: true, detailedTooltip: true, palette: { type: 'palette', diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.test.tsx index 59c03e02ac9f4..7fedd38e4e7ec 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.test.tsx @@ -105,6 +105,26 @@ describe('PointSeries Editor', function () { }); }); + it('not renders the long legend options if showElasticChartsOptions is false', async () => { + component = mountWithIntl(); + await act(async () => { + expect(findTestSubject(component, 'xyLongLegendsOptions').length).toBe(0); + }); + }); + + it('renders the long legend options if showElasticChartsOptions is true', async () => { + const newVisProps = ({ + ...props, + extraProps: { + showElasticChartsOptions: true, + }, + } as unknown) as PointSeriesOptionsProps; + component = mountWithIntl(); + await act(async () => { + expect(findTestSubject(component, 'xyLongLegendsOptions').length).toBe(1); + }); + }); + it('not renders the fitting function for a bar chart', async () => { const newVisProps = ({ ...props, diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx index 343976651d21e..1fd9b043e87f5 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx @@ -11,7 +11,11 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { BasicOptions, SwitchOption } from '../../../../../../vis_default_editor/public'; +import { + BasicOptions, + SwitchOption, + LongLegendOptions, +} from '../../../../../../vis_default_editor/public'; import { BUCKET_TYPES } from '../../../../../../data/public'; import { VisParams } from '../../../../types'; @@ -58,6 +62,14 @@ export function PointSeriesOptions( + {props.extraProps?.showElasticChartsOptions && ( + + )} {vis.data.aggs!.aggs.some( (agg) => agg.schema === 'segment' && agg.type.name === BUCKET_TYPES.DATE_HISTOGRAM diff --git a/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts b/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts index 35f3b2d7c627d..6d2b860066b07 100644 --- a/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts +++ b/src/plugins/vis_type_xy/public/expression_functions/xy_vis_fn.ts @@ -55,6 +55,18 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ defaultMessage: 'Show time marker', }), }, + truncateLegend: { + types: ['boolean'], + help: i18n.translate('visTypeXy.function.args.truncateLegend.help', { + defaultMessage: 'Defines if the legend will be truncated or not', + }), + }, + maxLegendLines: { + types: ['number'], + help: i18n.translate('visTypeXy.function.args.args.maxLegendLines.help', { + defaultMessage: 'Defines the maximum lines per legend item', + }), + }, addLegend: { types: ['boolean'], help: i18n.translate('visTypeXy.function.args.addLegend.help', { @@ -225,6 +237,8 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ addTooltip: args.addTooltip, legendPosition: args.legendPosition, addTimeMarker: args.addTimeMarker, + maxLegendLines: args.maxLegendLines, + truncateLegend: args.truncateLegend, categoryAxes: args.categoryAxes.map((categoryAxis) => ({ ...categoryAxis, type: categoryAxis.axisType, diff --git a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts index 8fafd4c723055..7fff29edfab51 100644 --- a/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_type_xy/public/sample_vis.test.mocks.ts @@ -88,6 +88,8 @@ export const sampleAreaVis = { legendPosition: 'right', times: [], addTimeMarker: false, + truncateLegend: true, + maxLegendLines: 1, thresholdLine: { show: false, value: 10, @@ -255,6 +257,8 @@ export const sampleAreaVis = { legendPosition: 'top', times: [], addTimeMarker: false, + truncateLegend: true, + maxLegendLines: 1, thresholdLine: { show: false, value: 10, diff --git a/src/plugins/vis_type_xy/public/to_ast.ts b/src/plugins/vis_type_xy/public/to_ast.ts index 9fec3f99ab39b..0b1eb5262d71a 100644 --- a/src/plugins/vis_type_xy/public/to_ast.ts +++ b/src/plugins/vis_type_xy/public/to_ast.ts @@ -194,6 +194,8 @@ export const toExpressionAst: VisToExpressionAst = async (vis, params type: vis.type.name as XyVisType, chartType: vis.params.type, addTimeMarker: vis.params.addTimeMarker, + truncateLegend: vis.params.truncateLegend, + maxLegendLines: vis.params.maxLegendLines, addLegend: vis.params.addLegend, addTooltip: vis.params.addTooltip, legendPosition: vis.params.legendPosition, diff --git a/src/plugins/vis_type_xy/public/types/param.ts b/src/plugins/vis_type_xy/public/types/param.ts index 421690f7fc6a9..0687bd2af2cd1 100644 --- a/src/plugins/vis_type_xy/public/types/param.ts +++ b/src/plugins/vis_type_xy/public/types/param.ts @@ -121,6 +121,8 @@ export interface VisParams { addTooltip: boolean; legendPosition: Position; addTimeMarker: boolean; + truncateLegend: boolean; + maxLegendLines: number; categoryAxes: CategoryAxis[]; orderBucketsBySum?: boolean; labels: Labels; @@ -158,6 +160,8 @@ export interface XYVisConfig { addTooltip: boolean; legendPosition: Position; addTimeMarker: boolean; + truncateLegend: boolean; + maxLegendLines: number; orderBucketsBySum?: boolean; labels: ExpressionValueLabel; thresholdLine: ExpressionValueThresholdLine; diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx index 2dffabb2ba0b9..346f6cc74a1ac 100644 --- a/src/plugins/vis_type_xy/public/vis_component.tsx +++ b/src/plugins/vis_type_xy/public/vis_component.tsx @@ -345,6 +345,8 @@ const VisComponent = (props: VisComponentProps) => { />