[Lens] Legend config (#70619)

This commit is contained in:
Joe Reuter 2020-07-22 12:14:59 +02:00 committed by GitHub
parent 78ea171a80
commit 3709de64d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 498 additions and 244 deletions

View file

@ -63,7 +63,7 @@ export function WorkspacePanelWrapper({
clearStagedPreview: false,
});
},
[dispatch]
[dispatch, activeVisualization]
);
return (
<>

View file

@ -13,7 +13,7 @@ import { toExpression, toPreviewExpression } from './to_expression';
import { LayerState, PieVisualizationState } from './types';
import { suggestions } from './suggestions';
import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants';
import { SettingsWidget } from './settings_widget';
import { PieToolbar } from './toolbar';
function newLayerState(layerId: string): LayerState {
return {
@ -204,10 +204,10 @@ export const pieVisualization: Visualization<PieVisualizationState, PieVisualiza
toExpression,
toPreviewExpression,
renderLayerContextMenu(domElement, props) {
renderToolbar(domElement, props) {
render(
<I18nProvider>
<SettingsWidget {...props} />
<PieToolbar {...props} />
</I18nProvider>,
domElement
);

View file

@ -7,6 +7,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { i18n } from '@kbn/i18n';
import { Position } from '@elastic/charts';
import { I18nProvider } from '@kbn/i18n/react';
import {
IInterpreterRenderHandlers,
@ -73,6 +74,11 @@ export const pie: ExpressionFunctionDefinition<
types: ['boolean'],
help: '',
},
legendPosition: {
types: ['string'],
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
help: '',
},
percentDecimals: {
types: ['number'],
help: '',

View file

@ -65,6 +65,13 @@ describe('PieVisualization component', () => {
};
}
test('it shows legend on correct side', () => {
const component = shallow(
<PieComponent args={{ ...args, legendPosition: 'top' }} {...getDefaultArgs()} />
);
expect(component.find(Settings).prop('legendPosition')).toEqual('top');
});
test('it shows legend for 2 groups using default legendDisplay', () => {
const component = shallow(<PieComponent args={args} {...getDefaultArgs()} />);
expect(component.find(Settings).prop('showLegend')).toEqual(true);

View file

@ -22,6 +22,7 @@ import {
PartitionFillLabel,
RecursivePartial,
LayerValue,
Position,
} from '@elastic/charts';
import { FormatFactory, LensFilterEvent } from '../types';
import { VisualizationContainer } from '../visualization_container';
@ -55,6 +56,7 @@ export function PieComponent(
numberDisplay,
categoryDisplay,
legendDisplay,
legendPosition,
nestedLegend,
percentDecimals,
hideLabels,
@ -237,6 +239,7 @@ export function PieComponent(
(legendDisplay === 'show' ||
(legendDisplay === 'default' && columnGroups.length > 1 && shape !== 'treemap'))
}
legendPosition={legendPosition || Position.Right}
legendMaxDepth={nestedLegend ? undefined : 1 /* Color is based only on first layer */}
onElementClick={(args) => {
const context = getFilterContext(

View file

@ -1,3 +0,0 @@
.lnsPieSettingsWidget {
min-width: $euiSizeXL * 10;
}

View file

@ -1,230 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiForm,
EuiFormRow,
EuiSuperSelect,
EuiRange,
EuiSwitch,
EuiHorizontalRule,
EuiSpacer,
EuiButtonGroup,
} from '@elastic/eui';
import { DEFAULT_PERCENT_DECIMALS } from './constants';
import { PieVisualizationState, SharedLayerState } from './types';
import { VisualizationLayerWidgetProps } from '../types';
import './settings_widget.scss';
const numberOptions: Array<{ value: SharedLayerState['numberDisplay']; inputDisplay: string }> = [
{
value: 'hidden',
inputDisplay: i18n.translate('xpack.lens.pieChart.hiddenNumbersLabel', {
defaultMessage: 'Hide from chart',
}),
},
{
value: 'percent',
inputDisplay: i18n.translate('xpack.lens.pieChart.showPercentValuesLabel', {
defaultMessage: 'Show percent',
}),
},
{
value: 'value',
inputDisplay: i18n.translate('xpack.lens.pieChart.showFormatterValuesLabel', {
defaultMessage: 'Show value',
}),
},
];
const categoryOptions: Array<{
value: SharedLayerState['categoryDisplay'];
inputDisplay: string;
}> = [
{
value: 'default',
inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', {
defaultMessage: 'Inside or outside',
}),
},
{
value: 'inside',
inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', {
defaultMessage: 'Inside only',
}),
},
{
value: 'hide',
inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
defaultMessage: 'Hide labels',
}),
},
];
const categoryOptionsTreemap: Array<{
value: SharedLayerState['categoryDisplay'];
inputDisplay: string;
}> = [
{
value: 'default',
inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', {
defaultMessage: 'Show labels',
}),
},
{
value: 'hide',
inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
defaultMessage: 'Hide labels',
}),
},
];
const legendOptions: Array<{
value: SharedLayerState['legendDisplay'];
label: string;
id: string;
}> = [
{
id: 'pieLegendDisplay-default',
value: 'default',
label: i18n.translate('xpack.lens.pieChart.defaultLegendLabel', {
defaultMessage: 'auto',
}),
},
{
id: 'pieLegendDisplay-show',
value: 'show',
label: i18n.translate('xpack.lens.pieChart.alwaysShowLegendLabel', {
defaultMessage: 'show',
}),
},
{
id: 'pieLegendDisplay-hide',
value: 'hide',
label: i18n.translate('xpack.lens.pieChart.hideLegendLabel', {
defaultMessage: 'hide',
}),
},
];
export function SettingsWidget(props: VisualizationLayerWidgetProps<PieVisualizationState>) {
const { state, setState } = props;
const layer = state.layers[0];
if (!layer) {
return null;
}
return (
<EuiForm className="lnsPieSettingsWidget">
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.labelPositionLabel', {
defaultMessage: 'Label position',
})}
fullWidth
display="columnCompressed"
>
<EuiSuperSelect
compressed
valueOfSelected={layer.categoryDisplay}
options={state.shape === 'treemap' ? categoryOptionsTreemap : categoryOptions}
onChange={(option) => {
setState({
...state,
layers: [{ ...layer, categoryDisplay: option }],
});
}}
/>
</EuiFormRow>
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.numberLabels', {
defaultMessage: 'Label values',
})}
fullWidth
display="columnCompressed"
>
<EuiSuperSelect
compressed
disabled={layer.categoryDisplay === 'hide'}
valueOfSelected={layer.categoryDisplay === 'hide' ? 'hidden' : layer.numberDisplay}
options={numberOptions}
onChange={(option) => {
setState({
...state,
layers: [{ ...layer, numberDisplay: option }],
});
}}
/>
</EuiFormRow>
<EuiHorizontalRule margin="s" />
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.percentDecimalsLabel', {
defaultMessage: 'Decimal places for percent',
})}
fullWidth
display="columnCompressed"
>
<EuiRange
data-test-subj="indexPattern-dimension-formatDecimals"
value={layer.percentDecimals ?? DEFAULT_PERCENT_DECIMALS}
min={0}
max={10}
showInput
compressed
onChange={(e) => {
setState({
...state,
layers: [{ ...layer, percentDecimals: Number(e.currentTarget.value) }],
});
}}
/>
</EuiFormRow>
<EuiHorizontalRule margin="s" />
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.legendDisplayLabel', {
defaultMessage: 'Legend display',
})}
display="columnCompressed"
>
<div>
<EuiButtonGroup
legend={i18n.translate('xpack.lens.pieChart.legendDisplayLegend', {
defaultMessage: 'Legend display',
})}
options={legendOptions}
idSelected={legendOptions.find(({ value }) => value === layer.legendDisplay)!.id}
onChange={(optionId) => {
setState({
...state,
layers: [
{
...layer,
legendDisplay: legendOptions.find(({ id }) => id === optionId)!.value,
},
],
});
}}
buttonSize="compressed"
isFullWidth
/>
<EuiSpacer size="m" />
<EuiSwitch
compressed
label={i18n.translate('xpack.lens.pieChart.nestedLegendLabel', {
defaultMessage: 'Nested legend',
})}
checked={!!layer.nestedLegend}
onChange={() => {
setState({ ...state, layers: [{ ...layer, nestedLegend: !layer.nestedLegend }] });
}}
/>
</div>
</EuiFormRow>
</EuiForm>
);
}

View file

@ -41,6 +41,7 @@ function expressionHelper(
numberDisplay: [layer.numberDisplay],
categoryDisplay: [layer.categoryDisplay],
legendDisplay: [layer.legendDisplay],
legendPosition: [layer.legendPosition || 'right'],
percentDecimals: [layer.percentDecimals ?? DEFAULT_PERCENT_DECIMALS],
nestedLegend: [!!layer.nestedLegend],
},

View file

@ -0,0 +1,3 @@
.lnsPieToolbar__popover {
width: $euiFormMaxWidth;
}

View file

@ -0,0 +1,281 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './toolbar.scss';
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
EuiSelect,
EuiFormRow,
EuiSuperSelect,
EuiRange,
EuiSwitch,
EuiHorizontalRule,
EuiSpacer,
EuiButtonGroup,
} from '@elastic/eui';
import { Position } from '@elastic/charts';
import { DEFAULT_PERCENT_DECIMALS } from './constants';
import { PieVisualizationState, SharedLayerState } from './types';
import { VisualizationToolbarProps } from '../types';
import { ToolbarButton } from '../toolbar_button';
const numberOptions: Array<{ value: SharedLayerState['numberDisplay']; inputDisplay: string }> = [
{
value: 'hidden',
inputDisplay: i18n.translate('xpack.lens.pieChart.hiddenNumbersLabel', {
defaultMessage: 'Hide from chart',
}),
},
{
value: 'percent',
inputDisplay: i18n.translate('xpack.lens.pieChart.showPercentValuesLabel', {
defaultMessage: 'Show percent',
}),
},
{
value: 'value',
inputDisplay: i18n.translate('xpack.lens.pieChart.showFormatterValuesLabel', {
defaultMessage: 'Show value',
}),
},
];
const categoryOptions: Array<{
value: SharedLayerState['categoryDisplay'];
inputDisplay: string;
}> = [
{
value: 'default',
inputDisplay: i18n.translate('xpack.lens.pieChart.showCategoriesLabel', {
defaultMessage: 'Inside or outside',
}),
},
{
value: 'inside',
inputDisplay: i18n.translate('xpack.lens.pieChart.fitInsideOnlyLabel', {
defaultMessage: 'Inside only',
}),
},
{
value: 'hide',
inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
defaultMessage: 'Hide labels',
}),
},
];
const categoryOptionsTreemap: Array<{
value: SharedLayerState['categoryDisplay'];
inputDisplay: string;
}> = [
{
value: 'default',
inputDisplay: i18n.translate('xpack.lens.pieChart.showTreemapCategoriesLabel', {
defaultMessage: 'Show labels',
}),
},
{
value: 'hide',
inputDisplay: i18n.translate('xpack.lens.pieChart.categoriesInLegendLabel', {
defaultMessage: 'Hide labels',
}),
},
];
const legendOptions: Array<{
value: SharedLayerState['legendDisplay'];
label: string;
id: string;
}> = [
{
id: 'pieLegendDisplay-default',
value: 'default',
label: i18n.translate('xpack.lens.pieChart.legendVisibility.auto', {
defaultMessage: 'auto',
}),
},
{
id: 'pieLegendDisplay-show',
value: 'show',
label: i18n.translate('xpack.lens.pieChart.legendVisibility.show', {
defaultMessage: 'show',
}),
},
{
id: 'pieLegendDisplay-hide',
value: 'hide',
label: i18n.translate('xpack.lens.pieChart.legendVisibility.hide', {
defaultMessage: 'hide',
}),
},
];
export function PieToolbar(props: VisualizationToolbarProps<PieVisualizationState>) {
const [open, setOpen] = useState(false);
const { state, setState } = props;
const layer = state.layers[0];
if (!layer) {
return null;
}
return (
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiPopover
panelClassName="lnsPieToolbar__popover"
button={
<ToolbarButton
fontWeight="normal"
onClick={() => {
setOpen(!open);
}}
>
{i18n.translate('xpack.lens.pieChart.settingsLabel', { defaultMessage: 'Settings' })}
</ToolbarButton>
}
isOpen={open}
closePopover={() => {
setOpen(false);
}}
anchorPosition="downRight"
>
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.labelPositionLabel', {
defaultMessage: 'Label position',
})}
fullWidth
display="columnCompressed"
>
<EuiSuperSelect
compressed
valueOfSelected={layer.categoryDisplay}
options={state.shape === 'treemap' ? categoryOptionsTreemap : categoryOptions}
onChange={(option) => {
setState({
...state,
layers: [{ ...layer, categoryDisplay: option }],
});
}}
/>
</EuiFormRow>
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.numberLabels', {
defaultMessage: 'Label values',
})}
fullWidth
display="columnCompressed"
>
<EuiSuperSelect
compressed
disabled={layer.categoryDisplay === 'hide'}
valueOfSelected={layer.categoryDisplay === 'hide' ? 'hidden' : layer.numberDisplay}
options={numberOptions}
onChange={(option) => {
setState({
...state,
layers: [{ ...layer, numberDisplay: option }],
});
}}
/>
</EuiFormRow>
<EuiHorizontalRule margin="s" />
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.percentDecimalsLabel', {
defaultMessage: 'Decimal places for percent',
})}
fullWidth
display="columnCompressed"
>
<EuiRange
data-test-subj="indexPattern-dimension-formatDecimals"
value={layer.percentDecimals ?? DEFAULT_PERCENT_DECIMALS}
min={0}
max={10}
showInput
compressed
onChange={(e) => {
setState({
...state,
layers: [{ ...layer, percentDecimals: Number(e.currentTarget.value) }],
});
}}
/>
</EuiFormRow>
<EuiHorizontalRule margin="s" />
<EuiFormRow
label={i18n.translate('xpack.lens.pieChart.legendDisplayLabel', {
defaultMessage: 'Legend display',
})}
display="columnCompressed"
>
<div>
<EuiButtonGroup
legend={i18n.translate('xpack.lens.pieChart.legendDisplayLegend', {
defaultMessage: 'Legend display',
})}
options={legendOptions}
idSelected={legendOptions.find(({ value }) => value === layer.legendDisplay)!.id}
onChange={(optionId) => {
setState({
...state,
layers: [
{
...layer,
legendDisplay: legendOptions.find(({ id }) => id === optionId)!.value,
},
],
});
}}
buttonSize="compressed"
isFullWidth
/>
<EuiSpacer size="s" />
<EuiSwitch
compressed
label={i18n.translate('xpack.lens.pieChart.nestedLegendLabel', {
defaultMessage: 'Nested legend',
})}
disabled={layer.legendDisplay === 'hide'}
checked={!!layer.nestedLegend}
onChange={() => {
setState({ ...state, layers: [{ ...layer, nestedLegend: !layer.nestedLegend }] });
}}
/>
</div>
</EuiFormRow>
<EuiFormRow
display="columnCompressed"
label={i18n.translate('xpack.lens.xyChart.legendPositionLabel', {
defaultMessage: 'Legend position',
})}
>
<EuiSelect
compressed
disabled={layer.legendDisplay === 'hide'}
options={[
{ value: Position.Top, text: 'Top' },
{ value: Position.Left, text: 'Left' },
{ value: Position.Right, text: 'Right' },
{ value: Position.Bottom, text: 'Bottom' },
]}
value={layer.legendPosition || Position.Right}
onChange={(e) => {
setState({
...state,
layers: [{ ...layer, legendPosition: e.target.value as Position }],
});
}}
/>
</EuiFormRow>
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -13,6 +13,7 @@ export interface SharedLayerState {
numberDisplay: 'hidden' | 'percent' | 'value';
categoryDisplay: 'default' | 'inside' | 'hide';
legendDisplay: 'default' | 'show' | 'hide';
legendPosition?: 'left' | 'right' | 'top' | 'bottom';
nestedLegend?: boolean;
percentDecimals?: number;
}

View file

@ -64,6 +64,7 @@ Object {
"position": Array [
"bottom",
],
"showSingleSeries": Array [],
},
"function": "lens_xy_legendConfig",
"type": "function",

View file

@ -127,6 +127,9 @@ export const buildExpression = (
function: 'lens_xy_legendConfig',
arguments: {
isVisible: [state.legend.isVisible],
showSingleSeries: state.legend.showSingleSeries
? [state.legend.showSingleSeries]
: [],
position: [state.legend.position],
},
},

View file

@ -19,8 +19,18 @@ import { VisualizationType } from '../index';
import { FittingFunction } from './fitting_functions';
export interface LegendConfig {
/**
* Flag whether the legend should be shown. If there is just a single series, it will be hidden
*/
isVisible: boolean;
/**
* Position of the legend relative to the chart
*/
position: Position;
/**
* Flag whether the legend should be shown even with just a single series
*/
showSingleSeries?: boolean;
}
type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' };
@ -50,6 +60,12 @@ export const legendConfig: ExpressionFunctionDefinition<
defaultMessage: 'Specifies the legend position.',
}),
},
showSingleSeries: {
types: ['boolean'],
help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', {
defaultMessage: 'Specifies whether a legend with just a single entry should be shown',
}),
},
},
fn: function fn(input: unknown, args: LegendConfig) {
return {

View file

@ -7,6 +7,7 @@
import './xy_config_panel.scss';
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import { Position } from '@elastic/charts';
import { debounce } from 'lodash';
import {
EuiButtonGroup,
@ -16,12 +17,14 @@ import {
EuiFormRow,
EuiPopover,
EuiText,
EuiSelect,
htmlIdGenerator,
EuiForm,
EuiColorPicker,
EuiColorPickerProps,
EuiToolTip,
EuiIcon,
EuiHorizontalRule,
} from '@elastic/eui';
import {
VisualizationLayerWidgetProps,
@ -46,6 +49,30 @@ function updateLayer(state: State, layer: UnwrapArray<State['layers']>, index: n
};
}
const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [
{
id: `xy_legend_auto`,
value: 'auto',
label: i18n.translate('xpack.lens.xyChart.legendVisibility.auto', {
defaultMessage: 'auto',
}),
},
{
id: `xy_legend_show`,
value: 'show',
label: i18n.translate('xpack.lens.xyChart.legendVisibility.show', {
defaultMessage: 'show',
}),
},
{
id: `xy_legend_hide`,
value: 'hide',
label: i18n.translate('xpack.lens.xyChart.legendVisibility.hide', {
defaultMessage: 'hide',
}),
},
];
export function LayerContextMenu(props: VisualizationLayerWidgetProps<State>) {
const { state, layerId } = props;
const horizontalOnly = isHorizontalChart(state.layers);
@ -95,6 +122,12 @@ export function XyToolbar(props: VisualizationToolbarProps<State>) {
const hasNonBarSeries = props.state?.layers.some(
(layer) => layer.seriesType === 'line' || layer.seriesType === 'area'
);
const legendMode =
props.state?.legend.isVisible && !props.state?.legend.showSingleSeries
? 'auto'
: !props.state?.legend.isVisible
? 'hide'
: 'show';
return (
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
@ -157,6 +190,67 @@ export function XyToolbar(props: VisualizationToolbarProps<State>) {
/>
</EuiFormRow>
</EuiToolTip>
<EuiHorizontalRule margin="s" />
<EuiFormRow
display="columnCompressed"
label={i18n.translate('xpack.lens.xyChart.legendVisibilityLabel', {
defaultMessage: 'Legend display',
})}
>
<EuiButtonGroup
isFullWidth
legend={i18n.translate('xpack.lens.xyChart.legendVisibilityLabel', {
defaultMessage: 'Legend display',
})}
name="legendDisplay"
buttonSize="compressed"
options={legendOptions}
idSelected={legendOptions.find(({ value }) => value === legendMode)!.id}
onChange={(optionId) => {
const newMode = legendOptions.find(({ id }) => id === optionId)!.value;
if (newMode === 'auto') {
props.setState({
...props.state,
legend: { ...props.state.legend, isVisible: true, showSingleSeries: false },
});
} else if (newMode === 'show') {
props.setState({
...props.state,
legend: { ...props.state.legend, isVisible: true, showSingleSeries: true },
});
} else if (newMode === 'hide') {
props.setState({
...props.state,
legend: { ...props.state.legend, isVisible: false, showSingleSeries: false },
});
}
}}
/>
</EuiFormRow>
<EuiFormRow
display="columnCompressed"
label={i18n.translate('xpack.lens.xyChart.legendPositionLabel', {
defaultMessage: 'Legend position',
})}
>
<EuiSelect
disabled={legendMode === 'hide'}
compressed
options={[
{ value: Position.Top, text: 'Top' },
{ value: Position.Left, text: 'Left' },
{ value: Position.Right, text: 'Right' },
{ value: Position.Bottom, text: 'Bottom' },
]}
value={props.state?.legend.position}
onChange={(e) => {
props.setState({
...props.state,
legend: { ...props.state.legend, position: e.target.value as Position },
});
}}
/>
</EuiFormRow>
</EuiPopover>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -1556,6 +1556,73 @@ describe('xy_expression', () => {
expect(component.find(Settings).prop('showLegend')).toEqual(true);
});
test('it should always show legend if showSingleSeries is set', () => {
const { data, args } = sampleArgs();
const component = shallow(
<XYChart
data={{ ...data }}
args={{
...args,
layers: [{ ...args.layers[0], accessors: ['a'], splitAccessor: undefined }],
legend: { ...args.legend, isVisible: true, showSingleSeries: true },
}}
formatFactory={getFormatSpy}
timeZone="UTC"
chartsThemeService={chartsThemeService}
histogramBarTarget={50}
onClickValue={onClickValue}
onSelectRange={onSelectRange}
/>
);
expect(component.find(Settings).prop('showLegend')).toEqual(true);
});
test('it not show legend if isVisible is set to false', () => {
const { data, args } = sampleArgs();
const component = shallow(
<XYChart
data={{ ...data }}
args={{
...args,
legend: { ...args.legend, isVisible: false },
}}
formatFactory={getFormatSpy}
timeZone="UTC"
chartsThemeService={chartsThemeService}
histogramBarTarget={50}
onClickValue={onClickValue}
onSelectRange={onSelectRange}
/>
);
expect(component.find(Settings).prop('showLegend')).toEqual(false);
});
test('it should show legend on right side', () => {
const { data, args } = sampleArgs();
const component = shallow(
<XYChart
data={{ ...data }}
args={{
...args,
legend: { ...args.legend, position: 'top' },
}}
formatFactory={getFormatSpy}
timeZone="UTC"
chartsThemeService={chartsThemeService}
histogramBarTarget={50}
onClickValue={onClickValue}
onSelectRange={onSelectRange}
/>
);
expect(component.find(Settings).prop('legendPosition')).toEqual('top');
});
test('it should apply the fitting function to all non-bar series', () => {
const data: LensMultiTable = {
type: 'lens_multitable',

View file

@ -282,7 +282,11 @@ export function XYChart({
return (
<Chart>
<Settings
showLegend={legend.isVisible ? chartHasMoreThanOneSeries : legend.isVisible}
showLegend={
legend.isVisible && !legend.showSingleSeries
? chartHasMoreThanOneSeries
: legend.isVisible
}
legendPosition={legend.position}
showLegendExtra={false}
theme={chartTheme}

View file

@ -8569,15 +8569,15 @@
"xpack.lens.pie.treemaplabel": "ツリーマップ",
"xpack.lens.pie.treemapSuggestionLabel": "ツリーマップとして",
"xpack.lens.pie.visualizationName": "パイ",
"xpack.lens.pieChart.alwaysShowLegendLabel": "表示",
"xpack.lens.pieChart.categoriesInLegendLabel": "ラベルを非表示",
"xpack.lens.pieChart.defaultLegendLabel": "自動",
"xpack.lens.pieChart.fitInsideOnlyLabel": "内部のみ",
"xpack.lens.pieChart.hiddenNumbersLabel": "グラフから非表示",
"xpack.lens.pieChart.hideLegendLabel": "非表示",
"xpack.lens.pieChart.labelPositionLabel": "ラベル位置",
"xpack.lens.pieChart.legendDisplayLabel": "凡例表示",
"xpack.lens.pieChart.legendDisplayLegend": "凡例表示",
"xpack.lens.pieChart.legendVisibility.auto": "自動",
"xpack.lens.pieChart.legendVisibility.hide": "非表示",
"xpack.lens.pieChart.legendVisibility.show": "表示",
"xpack.lens.pieChart.nestedLegendLabel": "ネストされた凡例",
"xpack.lens.pieChart.numberLabels": "ラベル値",
"xpack.lens.pieChart.percentDecimalsLabel": "割合の小数点桁数",

View file

@ -8574,15 +8574,15 @@
"xpack.lens.pie.treemaplabel": "树状图",
"xpack.lens.pie.treemapSuggestionLabel": "为树状图",
"xpack.lens.pie.visualizationName": "饼图",
"xpack.lens.pieChart.alwaysShowLegendLabel": "显示",
"xpack.lens.pieChart.categoriesInLegendLabel": "隐藏标签",
"xpack.lens.pieChart.defaultLegendLabel": "自动",
"xpack.lens.pieChart.fitInsideOnlyLabel": "仅内部",
"xpack.lens.pieChart.hiddenNumbersLabel": "在图表中隐藏",
"xpack.lens.pieChart.hideLegendLabel": "隐藏",
"xpack.lens.pieChart.labelPositionLabel": "标签位置",
"xpack.lens.pieChart.legendDisplayLabel": "图例显示",
"xpack.lens.pieChart.legendDisplayLegend": "图例显示",
"xpack.lens.pieChart.legendVisibility.auto": "自动",
"xpack.lens.pieChart.legendVisibility.hide": "隐藏",
"xpack.lens.pieChart.legendVisibility.show": "显示",
"xpack.lens.pieChart.nestedLegendLabel": "嵌套图例",
"xpack.lens.pieChart.numberLabels": "标签值",
"xpack.lens.pieChart.percentDecimalsLabel": "百分比的小数位数",